Integrate OpenAdapt Execute¶
OpenAdapt Execute accepts one already-qualified transaction and processes it as a durable asynchronous execution. Your integration submits the exact qualification binding, keeps one idempotency key for the business transaction, and waits for a terminal receipt.
This guide is for an approved private-pilot partner. OpenAdapt supplies the service credential and the identifiers from the qualification pack.
Choose where execution runs¶
Execute can dispatch to an approved managed runner or to a customer-controlled runner. A customer-owned cloud runner and storage boundary can use the existing bring your own cloud (BYOC) connector. The customer runner executes the workflow. Cloud carries bounded authorization and control metadata and receives only the declared result and evidence allowed by the data boundary. Other customer-controlled deployments can use a workstation, server, or on-premises virtual machine without using BYOC.
Authenticate the service¶
OpenAdapt issues one service token. The token is restricted to one organization and a non-empty list of qualification identifiers.
Store the token in a server-side secret manager. Do not send it to a browser or mobile client. Send it only over HTTPS:
The production base URL is:
All resource examples below are relative to that base URL.
Create an execution¶
Send POST /v1/executions with an ExecuteRequestV1 body:
{
"schema_version": "openadapt.execute-request/v1",
"qualification_id": "qualification_12345678",
"workflow_version": "workflow_20260729",
"workflow_digest": "sha256:cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc",
"environment_id": "environment_12345678",
"parameters": {
"date": "2026-08-15",
"record": {
"id": "12345"
}
},
"idempotency_key": "caller_key_12345678",
"authorization_context": {
"actor_id": "caller_agent_12345678",
"authorization_reference": "authorization_12345678"
},
"effect_strength_schema_version": "1",
"minimum_effect_strength": "independent_system_of_record"
}
Use the qualification, workflow, digest, environment, parameter, and effect values from the qualification pack. The service refuses a request that does not match the active qualification exactly.
A successful submission returns HTTP 202:
{
"schema_version": "openadapt.execute-accepted/v1",
"execution_id": "018f5b5a-1f8d-7e20-8b70-4e0c8d9a4f21",
"state": "queued"
}
HTTP 202 means that OpenAdapt accepted the transaction for durable
processing. It does not mean that the business effect occurred.
Keep one idempotency key¶
Create one idempotency key for one business transaction. Retain the key with the transaction in your system.
- If an HTTP response is lost, send the exact same request and the same key. OpenAdapt returns the existing execution.
- If any request field changes, use a new transaction and a new key.
- OpenAdapt refuses a changed request under an existing key.
- Do not create a new key to repeat a transaction after an uncertain delivery
or a
reconciliation_requiredoutcome. Reconcile the possible effect first.
This rule prevents a network timeout from becoming a duplicate write.
Read the lifecycle state¶
Poll GET /v1/executions/{execution_id}. The response is an
ExecuteStatusV1:
{
"schema_version": "openadapt.execute-status/v1",
"execution_id": "018f5b5a-1f8d-7e20-8b70-4e0c8d9a4f21",
"state": "running",
"terminal_outcome": null,
"evidence_receipt_id": null,
"updated_at": "2026-07-29T12:00:15.000Z"
}
A lifecycle state reports current work. It does not report the final result.
| State | Integration action |
|---|---|
queued |
Wait for dispatch. |
running |
Wait while the runner acts or verifies. |
decision_required |
Let the authorized operator complete the signed decision task. |
waiting_for_reconciliation |
Do not repeat the write. Wait for the live effect check. |
terminal |
Read and validate the receipt. |
Only terminal carries terminal_outcome and evidence_receipt_id. A run can
return to running after a decision. Do not treat the state sequence as a
strict one-way list.
Read and validate the receipt¶
When the state is terminal, request
GET /v1/executions/{execution_id}/receipt. Before that point, the endpoint
returns HTTP 409. It also returns HTTP 409 if the terminal run still waits
for trusted evidence.
Validate the response with openadapt-types 0.9.0 or its published JSON
Schema. Then confirm these bindings in your application:
execution_idmatches the accepted execution.receipt_idmatches the status resource.workflow_digestmatches the submitted qualified workflow.outcomematchesterminal_outcome.- The receipt schema accepts all contract and effect-strength invariants.
- Store the full receipt with your transaction record.
Python validation is small:
from openadapt_types import ExecuteEvidenceReceiptV1, ExecuteStatusV1
status = ExecuteStatusV1.model_validate(status_json)
receipt = ExecuteEvidenceReceiptV1.model_validate(receipt_json)
assert receipt.execution_id == accepted_execution_id
assert receipt.receipt_id == status.evidence_receipt_id
assert receipt.workflow_digest == submitted_workflow_digest
assert receipt.outcome == status.terminal_outcome
Treat only verified as proof that the requested business effect passed its
complete configured contract.
| Outcome | Required action |
|---|---|
verified |
Accept the requested effect as verified. |
halted_before_effect |
Record the halt. The evidence established that no consequential effect occurred. |
reconciliation_required |
Do not repeat the write. Reconcile the possible or conflicting effect. |
rejected_policy |
Correct the authorization, qualification, identity, environment, or policy input. |
failed_platform |
The platform failed before any possible business effect. |
rolled_back_verified |
Record that the compensating effect passed. Do not record the original request as successful. |
The portable receipt contains evidence identifiers and contract results. The customer-controlled runner retains detailed screenshots, live identity values, and report bodies inside the declared boundary.
Receive signed webhooks¶
Execute sends these versioned event bodies:
execution.state_changedwith the currentExecuteStatusV1;execution.decision_requiredwith the signed decision task; andexecution.terminalwith theExecuteEvidenceReceiptV1.
OpenAdapt returns the webhook signing secret only during endpoint creation or
rotation. Store it in a secret manager. Select it by the signed
issuer_key_id.
For every delivery:
- Parse the body as one strict
openadapt.execute-webhook/v1event. - Remove only the top-level
signaturefield. - Serialize the remaining object as UTF-8 JSON with sorted keys, no optional whitespace, and ASCII escaping.
- Prefix those bytes with
openadapt.execute-webhook/v1and one null byte. - Calculate HMAC-SHA-256 with the endpoint signing secret.
- Compare the result with the body
signaturein constant time. - Reject the event if its schema, key identifier, or signature is invalid.
- Persist the event and its
event_idbefore you return a successful HTTP response.
The openadapt-types models implement the same canonicalization and
verify_hmac() check. Use them instead of maintaining a second algorithm when
possible.
Each delivery includes these transport headers:
Content-Type: application/json
User-Agent: OpenAdapt-Execute-Webhook/1.0
X-OpenAdapt-Event-Id: <opaque-event-id>
X-OpenAdapt-Delivery-Attempt: <positive-integer>
X-OpenAdapt-Event-Id and X-OpenAdapt-Delivery-Attempt are advisory copies
of the signed body event_id and delivery_attempt. The signed body is
authoritative. Select the secret with the signed body issuer_key_id and
verify the signed body signature; the signature is not an HTTP header.
Delivery, order, and retry rules¶
Webhook delivery is at least once. A delivery can arrive more than once or after a later state notification.
- Verify the signature before deduplication.
- Deduplicate by the signed
event_id. - Return a
2xxresponse only after durable persistence. - A timeout, network error, or retryable HTTP response can cause redelivery.
- Do not use arrival order to update the execution state.
- Fetch
GET /v1/executions/{execution_id}after a notification when you need the current state. - Treat a validated
execution.terminalreceipt, or the matching receipt resource, as authoritative. Never let a late state event replace it.
This pattern also covers a response loss after your endpoint accepted an
event: OpenAdapt can retry, and your event_id record makes the second delivery
safe.
Integration checklist¶
- Keep the service token and webhook secret on the server.
- Bind every request to the supplied qualification identifiers.
- Keep the same idempotency key and body across transport retries.
- Distinguish lifecycle states from terminal outcomes.
- Validate and store the terminal receipt.
- Never resubmit a possible write before reconciliation.
- Verify, persist, and deduplicate each webhook before acknowledgment.
For the product and qualification boundary, read the OpenAdapt Execute private-pilot guide.