# IWorkflowResponseStore

> Tracon documentation. Tracon is a .NET package family that adds a control
> plane on Microsoft Agent Framework, published as NuGet packages that run
> inside your own ASP.NET Core application.
>
> Page: https://tracon.dev/api/tracon.iworkflowresponsestore/
> Summary: IWorkflowResponseStore: Where the answers to a workflow's pending human requests are recorded: which run answered which request of which run, who answered, and whether the single answer.

<div class="api-provenance">Namespace Tracon · Assembly <code>Tracon.Abstractions.dll</code></div>

Where the answers to a workflow's pending human requests are recorded: which run
answered which request of which run, who answered, and whether the single answer a
[WorkflowResponseMode.SingleAnswer](/api/tracon.workflowresponsemode/) workflow allows is already taken.

```csharp
public interface IWorkflowResponseStore
```

## Remarks

The store keeps the decision, not its content: a record carries a SHA-256 of the
answer, never the answer itself. The request is still read from the waiting run's
[RunEventType.WorkflowRequest](/api/tracon.runeventtype/) event.

**Tenant behavior.** Reads and updates use the ambient tenant, read from
`ITenantContext`: [IWorkflowResponseStore.FindClaimAsync](/api/tracon.iworkflowresponsestore/#Tracon_IWorkflowResponseStore_FindClaimAsync_System_String_System_String_System_Threading_CancellationToken_), [IWorkflowResponseStore.ReplaceAsync](/api/tracon.iworkflowresponsestore/#Tracon_IWorkflowResponseStore_ReplaceAsync_System_Guid_Tracon_WorkflowResponseRecord_System_Threading_CancellationToken_) (the record
it abandons), [IWorkflowResponseStore.MarkDeliveredAsync](/api/tracon.iworkflowresponsestore/#Tracon_IWorkflowResponseStore_MarkDeliveredAsync_System_Guid_System_Threading_CancellationToken_), [IWorkflowResponseStore.ListBySourceRunAsync](/api/tracon.iworkflowresponsestore/#Tracon_IWorkflowResponseStore_ListBySourceRunAsync_System_Guid_System_Threading_CancellationToken_) and
[IWorkflowResponseStore.GetAsync](/api/tracon.iworkflowresponsestore/#Tracon_IWorkflowResponseStore_GetAsync_System_Guid_System_Threading_CancellationToken_) see only the caller's tenant. A write uses the expected tenant the
record carries ([WorkflowResponseRecord.TenantId](/api/tracon.workflowresponserecord/#Tracon_WorkflowResponseRecord_TenantId)). Two tenants can hold a claim
with the same scope and request id without seeing each other.

**DI lifetime.** Registered as a singleton with `TryAdd`; a consumer's own
registration wins, and each SQL provider replaces the in-memory default. An implementation
must be safe under concurrent calls: two answers to one request arrive together by design.

**Delivery.** No delivery guarantee applies: every call completes or throws
before it returns, and nothing is sent later. The respond endpoint refuses an answer whose
record cannot be written; the engine's [IWorkflowResponseStore.MarkDeliveredAsync](/api/tracon.iworkflowresponsestore/#Tracon_IWorkflowResponseStore_MarkDeliveredAsync_System_Guid_System_Threading_CancellationToken_) call is best-effort
and only logged when it fails.

A record is deleted only with its source run: a SQL store ties it to the run with a
cascading foreign key, and a data subject erasure that removes the source run removes its
records too. No retention target deletes runs, so the records of an installation grow
until such an erasure; plan for that the way you plan for the run history itself.

## Methods

### <a id="Tracon_IWorkflowResponseStore_AddAsync_Tracon_WorkflowResponseRecord_System_Threading_CancellationToken_"></a> AddAsync\(WorkflowResponseRecord, CancellationToken\)

Records an answer. When the record carries a [WorkflowResponseRecord.ClaimScope](/api/tracon.workflowresponserecord/#Tracon_WorkflowResponseRecord_ClaimScope),
it is written only if no other active record holds the same scope and request id.

```csharp
ValueTask<WorkflowResponseClaim> AddAsync(WorkflowResponseRecord record, CancellationToken cancellationToken = default)
```

#### Parameters

`record` [WorkflowResponseRecord](/api/tracon.workflowresponserecord/)

The answer to record. [WorkflowResponseRecord.Status](/api/tracon.workflowresponserecord/#Tracon_WorkflowResponseRecord_Status) must be
[WorkflowResponseStatus.Accepted](/api/tracon.workflowresponsestatus/) and [WorkflowResponseRecord.TenantId](/api/tracon.workflowresponserecord/#Tracon_WorkflowResponseRecord_TenantId)
must be set.

`cancellationToken` [CancellationToken](https://learn.microsoft.com/dotnet/api/system.threading.cancellationtoken)

The cancellation token.

#### Returns

 [ValueTask](https://learn.microsoft.com/dotnet/api/system.threading.tasks.valuetask\-1)<[WorkflowResponseClaim](/api/tracon.workflowresponseclaim/)\>

[WorkflowResponseClaim.Claimed](/api/tracon.workflowresponseclaim/#Tracon_WorkflowResponseClaim_Claimed) is [true](https://learn.microsoft.com/dotnet/csharp/language-reference/builtin-types/bool) when the record was
written. It is [false](https://learn.microsoft.com/dotnet/csharp/language-reference/builtin-types/bool) when another active record already holds the claim;
[WorkflowResponseClaim.Existing](/api/tracon.workflowresponseclaim/#Tracon_WorkflowResponseClaim_Existing) is that record.

#### Remarks

Two concurrent writes with the same claim are decided by the store, atomically: exactly
one of them is written. A record without a claim scope is always written.

### <a id="Tracon_IWorkflowResponseStore_FindClaimAsync_System_String_System_String_System_Threading_CancellationToken_"></a> FindClaimAsync\(string, string, CancellationToken\)

Finds the active record that holds a claim.

```csharp
ValueTask<WorkflowResponseRecord?> FindClaimAsync(string claimScope, string requestId, CancellationToken cancellationToken = default)
```

#### Parameters

`claimScope` [string](https://learn.microsoft.com/dotnet/api/system.string)

The claim scope (the execution session of the waiting run).

`requestId` [string](https://learn.microsoft.com/dotnet/api/system.string)

The request id.

`cancellationToken` [CancellationToken](https://learn.microsoft.com/dotnet/api/system.threading.cancellationtoken)

The cancellation token.

#### Returns

 [ValueTask](https://learn.microsoft.com/dotnet/api/system.threading.tasks.valuetask\-1)<[WorkflowResponseRecord](/api/tracon.workflowresponserecord/)?\>

The record, or [null](https://learn.microsoft.com/dotnet/csharp/language-reference/keywords/null) when no record of the caller's tenant that is not
[WorkflowResponseStatus.Abandoned](/api/tracon.workflowresponsestatus/) holds the claim.

### <a id="Tracon_IWorkflowResponseStore_GetAsync_System_Guid_System_Threading_CancellationToken_"></a> GetAsync\(Guid, CancellationToken\)

Returns the record of one answering run.

```csharp
ValueTask<WorkflowResponseRecord?> GetAsync(Guid responseRunId, CancellationToken cancellationToken = default)
```

#### Parameters

`responseRunId` [Guid](https://learn.microsoft.com/dotnet/api/system.guid)

The run that answered.

`cancellationToken` [CancellationToken](https://learn.microsoft.com/dotnet/api/system.threading.cancellationtoken)

The cancellation token.

#### Returns

 [ValueTask](https://learn.microsoft.com/dotnet/api/system.threading.tasks.valuetask\-1)<[WorkflowResponseRecord](/api/tracon.workflowresponserecord/)?\>

The record, or [null](https://learn.microsoft.com/dotnet/csharp/language-reference/keywords/null) when it is not found in the caller's tenant.

### <a id="Tracon_IWorkflowResponseStore_ListBySourceRunAsync_System_Guid_System_Threading_CancellationToken_"></a> ListBySourceRunAsync\(Guid, CancellationToken\)

Lists every answer given to a run's requests, oldest first.

```csharp
ValueTask<IReadOnlyList<WorkflowResponseRecord>> ListBySourceRunAsync(Guid sourceRunId, CancellationToken cancellationToken = default)
```

#### Parameters

`sourceRunId` [Guid](https://learn.microsoft.com/dotnet/api/system.guid)

The run that waited for the answers.

`cancellationToken` [CancellationToken](https://learn.microsoft.com/dotnet/api/system.threading.cancellationtoken)

The cancellation token.

#### Returns

 [ValueTask](https://learn.microsoft.com/dotnet/api/system.threading.tasks.valuetask\-1)<[IReadOnlyList](https://learn.microsoft.com/dotnet/api/system.collections.generic.ireadonlylist\-1)<[WorkflowResponseRecord](/api/tracon.workflowresponserecord/)\>\>

The records of the caller's tenant, ordered by [WorkflowResponseRecord.RespondedAt](/api/tracon.workflowresponserecord/#Tracon_WorkflowResponseRecord_RespondedAt).

### <a id="Tracon_IWorkflowResponseStore_MarkDeliveredAsync_System_Guid_System_Threading_CancellationToken_"></a> MarkDeliveredAsync\(Guid, CancellationToken\)

Records that the workflow received the answer: an [WorkflowResponseStatus.Accepted](/api/tracon.workflowresponsestatus/)
record becomes [WorkflowResponseStatus.Delivered](/api/tracon.workflowresponsestatus/).

```csharp
ValueTask MarkDeliveredAsync(Guid responseRunId, CancellationToken cancellationToken = default)
```

#### Parameters

`responseRunId` [Guid](https://learn.microsoft.com/dotnet/api/system.guid)

The run that delivered the answer.

`cancellationToken` [CancellationToken](https://learn.microsoft.com/dotnet/api/system.threading.cancellationtoken)

The cancellation token.

#### Returns

 [ValueTask](https://learn.microsoft.com/dotnet/api/system.threading.tasks.valuetask)

The completion task.

#### Remarks

Does nothing when no record of the caller's tenant has this run id, or when the record is
no longer [WorkflowResponseStatus.Accepted](/api/tracon.workflowresponsestatus/).

### <a id="Tracon_IWorkflowResponseStore_ReplaceAsync_System_Guid_Tracon_WorkflowResponseRecord_System_Threading_CancellationToken_"></a> ReplaceAsync\(Guid, WorkflowResponseRecord, CancellationToken\)

Abandons an active record and writes the record of the run that replaces it, as one
atomic step.

```csharp
ValueTask<bool> ReplaceAsync(Guid abandonedResponseRunId, WorkflowResponseRecord replacement, CancellationToken cancellationToken = default)
```

#### Parameters

`abandonedResponseRunId` [Guid](https://learn.microsoft.com/dotnet/api/system.guid)

The run id of the record to abandon.

`replacement` [WorkflowResponseRecord](/api/tracon.workflowresponserecord/)

The record that takes over the claim. [WorkflowResponseRecord.Status](/api/tracon.workflowresponserecord/#Tracon_WorkflowResponseRecord_Status) must be
[WorkflowResponseStatus.Accepted](/api/tracon.workflowresponsestatus/).

`cancellationToken` [CancellationToken](https://learn.microsoft.com/dotnet/api/system.threading.cancellationtoken)

The cancellation token.

#### Returns

 [ValueTask](https://learn.microsoft.com/dotnet/api/system.threading.tasks.valuetask\-1)<[bool](https://learn.microsoft.com/dotnet/api/system.boolean)\>

[true](https://learn.microsoft.com/dotnet/csharp/language-reference/builtin-types/bool) when the record was abandoned and `replacement` written;
[false](https://learn.microsoft.com/dotnet/csharp/language-reference/builtin-types/bool) when the record is not found in the caller's tenant or is already
[WorkflowResponseStatus.Abandoned](/api/tracon.workflowresponsestatus/), in which case nothing is written.

#### Remarks

Two concurrent calls that abandon the same record are decided by the store: one returns
[true](https://learn.microsoft.com/dotnet/csharp/language-reference/builtin-types/bool), the other [false](https://learn.microsoft.com/dotnet/csharp/language-reference/builtin-types/bool).
