Flow Studio
Correlation Tokens
The ExecutionResId (correlation token) is a GUID that uniquely identifies a specific HIL suspension instance. It is the key that links an actor's response — from any channel — back to the correct suspended execution.
Token Properties
| Property | Value |
|---|---|
| Format | UUID v4 (GUID) — e.g., 3f9c4b2a-7e8d-4c1f-a3b6-9d0e1f2a3b4c |
| Generated by | The HIL executor (inside BuildSuspendPayload) — not the engine |
| Uniqueness | Globally unique across all tenants, executions, and suspensions |
| Single-use | Marked consumed on first valid resume call — repeat use returns 409 Conflict |
| Lifetime | Permanent — never deleted (kept for audit even after use/expiry) |
Token in URLs
The correlation token appears in two URLs:
- Resume API:
POST /api/executions/{executionResId}/resume - Task detail page:
https://workdesk.bizfirstai.com/tasks/{executionResId}(embedded in notification emails/Slack messages)
Security Considerations
The token is a secret — anyone who knows it can resume the execution. The API additionally validates:
- The token exists in the database.
- The token has not been consumed (ResumedAt is null).
- The token has not expired (ExpiresAt is null or in the future).
- The calling actor's JWT TenantId matches the suspension's TenantId.
Tokens should only be transmitted over HTTPS and should not be logged in plain text. Notification emails should use short-lived signed links that wrap the token, not embed it directly in the URL.
Token Validation Code
public async Task<ValidatedToken> ValidateAsync(string executionResId, string actorId, string tenantId)
{
var suspension = await _repo.GetByExecutionResIdAsync(executionResId);
if (suspension == null)
throw new InvalidTokenException("Token not found");
if (suspension.TenantId != tenantId)
throw new InvalidTokenException("Tenant mismatch");
if (suspension.ResumedAt.HasValue)
throw new TokenAlreadyUsedException("Token already consumed");
if (suspension.ExpiresAt.HasValue && suspension.ExpiresAt < DateTimeOffset.UtcNow)
throw new TokenExpiredException("Token has expired");
return new ValidatedToken { Suspension = suspension };
}