Skip to content

Submit a terminal dispatch result

POST
/v1/participant-dispatches/{id}/results
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.

id
required
string format: uuid

The participant_dispatch_attempt row id — same id the coordinator stamped into the CloudEvents subject and into the outbound dispatch envelope.

Signed result_submit envelope (proposal 0022 §7.3).

Media typeapplication/json

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
data
required

Aerandil-specific payload (§5.5). Shape varies by type; the schema at dataschema is the contract.

object
datacontenttype
required
string
Allowed value: application/json
dataschema
required

URL of the schema describing data. v1 URLs are https://schema.aerandil.example/0022/...; receivers match on this to dispatch parsing.

string format: uri
id
required

UUID v7 per §5.1; reused across retries of the same logical dispatch.

string format: uuid
source
required

Producer URI; e.g. aerandil://coordinator/tenant=T-1/program=... or aerandil://participant/....

string
specversion
required

CloudEvents specversion — pinned at 1.0.

string
Allowed value: 1.0
subject
required

Composite key (e.g. <run_id>/<participation_id>/<slot>).

string
>= 1 characters
time
required

RFC 3339; checked against the ±5-minute replay window.

string format: date-time
type
required

Reverse-DNS message kind.

string
Allowed values: aerandil.participant_dispatch.dispatched aerandil.participant_dispatch.acknowledged aerandil.participant_dispatch.result_submitted aerandil.participant_dispatch.delivery_failed aerandil.participant_dispatch.quarantine_notice
data
required
object
dispatch_kind
required
string
>= 1 characters
envelope_version
required
string
Allowed value: 1.0
result
required
object
result_kind
required
string
>= 1 characters
result_status
required
string
Allowed values: accepted rejected_at_local_validator abandoned
signature
required

Ed25519 detached signature over the canonical bytes of the surrounding envelope (RFC 8785 JCS with the signature field elided). Proposal 0022 §8.2.

object
alg
required

Signature algorithm — v1 supports ed25519 only.

string
Allowed value: ed25519
kid
required

Key identifier. Resolves against federated_participation.runner_signing_key_ref for participant-signed envelopes, or the coordinator signing config for coordinator-signed.

string
>= 1 characters
value
required

Base64 (RFC 4648 §4) detached signature bytes.

string
>= 1 characters
type
required
Allowed value: aerandil.participant_dispatch.result_submitted

Result recorded (or idempotent replay against an already-recorded row).

Missing or invalid bearer credential.

Media typeapplication/json

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
error
required
object
code
required

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).

string
instance_id

Request-id correlation handle echoed on the request-id response header — support teams correlate without scraping logs.

string
message
required

Human-readable explanation of the failure.

string
parameters

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.

Media typeapplication/json

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
error
required
object
code
required

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).

string
instance_id

Request-id correlation handle echoed on the request-id response header — support teams correlate without scraping logs.

string
message
required

Human-readable explanation of the failure.

string
parameters

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).

Media typeapplication/json

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
error
required
object
code
required

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).

string
instance_id

Request-id correlation handle echoed on the request-id response header — support teams correlate without scraping logs.

string
message
required

Human-readable explanation of the failure.

string
parameters

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).

Media typeapplication/json

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
error
required
object
code
required

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).

string
instance_id

Request-id correlation handle echoed on the request-id response header — support teams correlate without scraping logs.

string
message
required

Human-readable explanation of the failure.

string
parameters

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).

Media typeapplication/json

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
error
required
object
code
required

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).

string
instance_id

Request-id correlation handle echoed on the request-id response header — support teams correlate without scraping logs.

string
message
required

Human-readable explanation of the failure.

string
parameters

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"
}
}