Submit a terminal dispatch result
const url = 'https://api.aerandil.com/v1/participant-dispatches/2489E9AD-2EE2-8E00-8EC9-32D5F69181C0/results';const options = { method: 'POST', headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'}, body: '{"data":{"dispatch_kind":"example","envelope_version":"1.0","result":{"result_kind":"example","result_status":"accepted"},"signature":{"alg":"ed25519","kid":"example","value":"example"}},"datacontenttype":"application/json","dataschema":"https://example.com","id":"2489E9AD-2EE2-8E00-8EC9-32D5F69181C0","source":"example","specversion":"1.0","subject":"example","time":"2026-04-15T12:00:00Z","type":"aerandil.participant_dispatch.result_submitted"}'};
try { const response = await fetch(url, options); const data = await response.json(); console.log(data);} catch (error) { console.error(error);}curl --request POST \ --url https://api.aerandil.com/v1/participant-dispatches/2489E9AD-2EE2-8E00-8EC9-32D5F69181C0/results \ --header 'Authorization: Bearer <token>' \ --header 'Content-Type: application/json' \ --data '{ "data": { "dispatch_kind": "example", "envelope_version": "1.0", "result": { "result_kind": "example", "result_status": "accepted" }, "signature": { "alg": "ed25519", "kid": "example", "value": "example" } }, "datacontenttype": "application/json", "dataschema": "https://example.com", "id": "2489E9AD-2EE2-8E00-8EC9-32D5F69181C0", "source": "example", "specversion": "1.0", "subject": "example", "time": "2026-04-15T12:00:00Z", "type": "aerandil.participant_dispatch.result_submitted" }'Participant → coordinator. Submits the terminal result of a dispatch. Maps 1:1 onto a record_federated_contribution action for federated mode, or record_workflow_step_result for workflow mode — both downstream actions enforce their own idempotency on the contribution / step fingerprint, so a re-delivered envelope with the same payload is a benign replay.
Envelope shape. A signed CloudEvents 1.0 envelope with type = aerandil.participant_dispatch.result_submitted. Same signature rules as the ack route.
Idempotency conflict. A re-delivered envelope with the same idempotency key but a different payload hash is rejected with 409 and the receiver emits an aerandil.participant_dispatch.delivery_failed event with failure_reason = idempotency_conflict.
Auth. Same service principal + scope as the ack route.
Authorizations
Section titled “Authorizations”Parameters
Section titled “Parameters”Path Parameters
Section titled “Path Parameters”The participant_dispatch_attempt row id — same id the coordinator stamped into the CloudEvents subject and into the outbound dispatch envelope.
Request Bodyrequired
Section titled “Request Bodyrequired”Signed result_submit envelope (proposal 0022 §7.3).
Envelope with type = aerandil.participant_dispatch.result_submitted (§7.3). Body of POST /v1/participant-dispatches/{id}/results. The data.result block is discriminated by result_kind; v1 supports federated_contribution (per /0022/result/federated_contribution.v1.json) and is extensible per dispatch_kind.
object
Aerandil-specific payload (§5.5). Shape varies by type; the schema at dataschema is the contract.
object
URL of the schema describing data. v1 URLs are https://schema.aerandil.example/0022/...; receivers match on this to dispatch parsing.
UUID v7 per §5.1; reused across retries of the same logical dispatch.
Producer URI; e.g. aerandil://coordinator/tenant=T-1/program=... or aerandil://participant/....
CloudEvents specversion — pinned at 1.0.
Composite key (e.g. <run_id>/<participation_id>/<slot>).
RFC 3339; checked against the ±5-minute replay window.
Reverse-DNS message kind.
object
object
Ed25519 detached signature over the canonical bytes of the surrounding envelope (RFC 8785 JCS with the signature field elided). Proposal 0022 §8.2.
object
Signature algorithm — v1 supports ed25519 only.
Key identifier. Resolves against federated_participation.runner_signing_key_ref for participant-signed envelopes, or the coordinator signing config for coordinator-signed.
Base64 (RFC 4648 §4) detached signature bytes.
Responses
Section titled “Responses”Result recorded (or idempotent replay against an already-recorded row).
Missing or invalid bearer credential.
Structured error wrapper. Every non-2xx response from any Aerandil endpoint serialises to this shape; the HTTP status carries the failure class (4xx/5xx), the body adds a stable code for programmatic dispatch plus a human-readable message.
object
object
Stable, programmatic error code (e.g. unauthorized, forbidden, not_found, invalid_body, invalid_url, precondition_failed, no_secondary_key). Clients dispatch on code, not on the message (which may be localised or contextualised in future versions).
Request-id correlation handle echoed on the request-id response header — support teams correlate without scraping logs.
Human-readable explanation of the failure.
Per-error structured detail. The shape varies by code (e.g. forbidden_action carries {"action": "..."}; precondition_failed carries {"current_version": N}).
Examplegenerated
{ "error": { "code": "example", "instance_id": "example", "message": "example", "parameters": "example" }}Principal lacks internal:participant_dispatch:receive scope, or is not a service principal.
Structured error wrapper. Every non-2xx response from any Aerandil endpoint serialises to this shape; the HTTP status carries the failure class (4xx/5xx), the body adds a stable code for programmatic dispatch plus a human-readable message.
object
object
Stable, programmatic error code (e.g. unauthorized, forbidden, not_found, invalid_body, invalid_url, precondition_failed, no_secondary_key). Clients dispatch on code, not on the message (which may be localised or contextualised in future versions).
Request-id correlation handle echoed on the request-id response header — support teams correlate without scraping logs.
Human-readable explanation of the failure.
Per-error structured detail. The shape varies by code (e.g. forbidden_action carries {"action": "..."}; precondition_failed carries {"current_version": N}).
Examplegenerated
{ "error": { "code": "example", "instance_id": "example", "message": "example", "parameters": "example" }}No dispatch row with that id (or row is in a different tenant).
Structured error wrapper. Every non-2xx response from any Aerandil endpoint serialises to this shape; the HTTP status carries the failure class (4xx/5xx), the body adds a stable code for programmatic dispatch plus a human-readable message.
object
object
Stable, programmatic error code (e.g. unauthorized, forbidden, not_found, invalid_body, invalid_url, precondition_failed, no_secondary_key). Clients dispatch on code, not on the message (which may be localised or contextualised in future versions).
Request-id correlation handle echoed on the request-id response header — support teams correlate without scraping logs.
Human-readable explanation of the failure.
Per-error structured detail. The shape varies by code (e.g. forbidden_action carries {"action": "..."}; precondition_failed carries {"current_version": N}).
Examplegenerated
{ "error": { "code": "example", "instance_id": "example", "message": "example", "parameters": "example" }}Illegal status transition, OR idempotency conflict (same key, different payload).
Structured error wrapper. Every non-2xx response from any Aerandil endpoint serialises to this shape; the HTTP status carries the failure class (4xx/5xx), the body adds a stable code for programmatic dispatch plus a human-readable message.
object
object
Stable, programmatic error code (e.g. unauthorized, forbidden, not_found, invalid_body, invalid_url, precondition_failed, no_secondary_key). Clients dispatch on code, not on the message (which may be localised or contextualised in future versions).
Request-id correlation handle echoed on the request-id response header — support teams correlate without scraping logs.
Human-readable explanation of the failure.
Per-error structured detail. The shape varies by code (e.g. forbidden_action carries {"action": "..."}; precondition_failed carries {"current_version": N}).
Examplegenerated
{ "error": { "code": "example", "instance_id": "example", "message": "example", "parameters": "example" }}Envelope parse / signature verification failed (malformed JSON, wrong type, expired time, signature mismatch).
Structured error wrapper. Every non-2xx response from any Aerandil endpoint serialises to this shape; the HTTP status carries the failure class (4xx/5xx), the body adds a stable code for programmatic dispatch plus a human-readable message.
object
object
Stable, programmatic error code (e.g. unauthorized, forbidden, not_found, invalid_body, invalid_url, precondition_failed, no_secondary_key). Clients dispatch on code, not on the message (which may be localised or contextualised in future versions).
Request-id correlation handle echoed on the request-id response header — support teams correlate without scraping logs.
Human-readable explanation of the failure.
Per-error structured detail. The shape varies by code (e.g. forbidden_action carries {"action": "..."}; precondition_failed carries {"current_version": N}).
Examplegenerated
{ "error": { "code": "example", "instance_id": "example", "message": "example", "parameters": "example" }}