Implementing your service
- Status
- Current
- Updated
- 2026-09-13
- Scope
- API Platform provider implementation and conformance checks
Your service exposes four endpoints. The platform calls them; you return an envelope with a fixed shape.
This page covers how to implement them, and what the platform checks once you submit a connection.
Four endpoints
GET /health/live the process is alive
GET /health/ready your own configuration and dependencies are usable
POST /v1/executions run one call
POST /v1/reconciliations look up what actually happened to a call
Health endpoints require no credential. The other two must verify the Authorization: Bearer credential the platform sends. That credential belongs to your connection alone — leaking it opens your execution endpoint to anyone.
Readiness reports on your availability. Do not probe the platform from it: when the platform is unavailable you neither need to nor should report yourself unready.
Execution
POST /v1/executions
Authorization: Bearer <credential the platform configured for you>
Idempotency-Key: <stable identifier generated by the platform>
Content-Type: application/json
{ "payload": { "fields you defined in your listing contract": "value" } }
payload is a non-empty JSON object whose contents your listing contract defines. The envelope itself has exactly this one top-level field, and any unknown top-level field must be rejected.
Response envelope
Success or failure, the shape is the same:
{
"outcome": "SUCCEEDED",
"correlationId": "your own job id",
"result": { "businessResult": true },
"errorCode": "",
"httpStatus": 0
}
Omit fields that do not apply. Four outcomes, and the HTTP status must match:
| outcome | HTTP status | Meaning | What the platform does |
|---|---|---|---|
SUCCEEDED | any 2xx | Business done and result persisted | Settles and charges |
DEFINED_FAILURE | the same 4xx/5xx as httpStatus | The request definitively failed | Releases the hold, no charge |
RETRYABLE_BEFORE_ACCEPT | exactly 503 | You can prove the call was not accepted | Bounded redispatch |
UNKNOWN_OUTCOME | exactly 202 | Possibly accepted, outcome undetermined | Reconciles only, never resends |
DEFINED_FAILURE needs a stable errorCode (64 characters or fewer).
Do not use DEFINED_FAILURE for infrastructure uncertainty. It means "this request definitively failed", and the platform releases the hold on that basis. If you had in fact already executed the work, that work goes unpaid. When uncertain, use UNKNOWN_OUTCOME.
Timeouts, dropped connections, malformed JSON, oversized bodies, status/outcome mismatches, redirects — the platform treats all of them as UNKNOWN_OUTCOME. So anything you have accepted must be discoverable through reconciliation.
Idempotency
Every call carries a stable Idempotency-Key. You need to:
- Fingerprint the raw
payloadbytes and store that fingerprint against the key; - On same key, same fingerprint, return the stored outcome without re-executing;
- On same key, different fingerprint, return
DEFINED_FAILUREwith bothhttpStatusand the HTTP status set to409, and leave the original record unchanged.
Storage must be durable. After a restart, reconciliation for the same key must still return what you accepted — an empty in-memory map is not evidence that nothing was accepted.
Order matters too: persist the "accepted" marker before the business side effect (or within the same atomic operation), persist the final outcome before responding, and write the HTTP response only after the transaction commits.
Reconciliation
POST /v1/reconciliations
{ "providerIdempotencyKey": "the key the platform used earlier" }
Reconciliation never starts execution. It only reads what you already stored.
- Found: return the currently stored outcome.
- Not found, and you can prove the key was never accepted:
RETRYABLE_BEFORE_ACCEPT. - Not found, but storage is unavailable or you are unsure:
UNKNOWN_OUTCOME.
Returning success for a key you cannot find is the most dangerous implementation. It claims work that never happened, and the platform charges the caller for it.
What the platform checks after you submit
Once a connection is submitted, the platform calls your endpoints and runs a contract conformance check. Results appear directly on your connection page, and you can re-run it yourself as often as you like — fix, re-run, see what is still red, without waiting on anyone.
The check produces one real call against your service. It uses a different Idempotency-Key prefix from live traffic (ekko-conformance:) so you can recognise it; that call creates no receipt and holds or charges nothing.
The check does not understand your business: it sends a deliberately meaningless payload, and treats SUCCEEDED and DEFINED_FAILURE as equally acceptable. It verifies envelope shape, idempotency semantics, and credential enforcement — not business success. Rejecting junk input cleanly with a DEFINED_FAILURE and an errorCode is itself proof of compliance.
The nine checks
| Check | Failing means |
|---|---|
| Liveness | /health/live unreachable or not 2xx |
| Readiness | /health/ready unreachable or not 2xx. Remember it must not depend on the platform |
| Execution requires credential | Your execution endpoint answers without a credential. Anyone can drive your business logic |
| Execution envelope valid | The outcome is not one of the four, or the HTTP status does not match it |
| Replay is stable | Same key and payload returned a different result, so the work ran twice |
| Fingerprint conflict | Same key with a different payload was not rejected with 409, so your idempotency record is not actually bound to the payload |
| Unknown envelope field rejected | You accepted a top-level field outside the contract. When the protocol evolves you will swallow new fields without understanding them |
| Known key reconciles | A key you just accepted cannot be found, or returns RETRYABLE_BEFORE_ACCEPT — the latter makes the platform execute it again |
| Unknown key does not claim success | Reconciling a never-submitted key returned SUCCEEDED |
About "skipped"
A check shown as skipped is neither passed nor failed — it means no evidence was gathered.
If the execution endpoint itself did not work, the three idempotency checks have no baseline to compare against and are all skipped. Do not go auditing your idempotency implementation at that point: fix the earlier failure first, then re-run to get a real verdict on those.
How the check relates to publication
Passing the technical check is a precondition for approval. A connection that has not passed will not be approved.
Changing the connection's address or code invalidates earlier results; re-run the check.
Passing is not the same as being listed. It only says the technical contract holds; whether a listing belongs in the catalog is a separate business judgement.
A minimal skeleton
Pseudocode for ordering; any language works:
POST /v1/executions:
verify Authorization, otherwise → 401
parse envelope, unknown top-level field → 400
fingerprint = hash of raw payload bytes
begin transaction
record = read by key, locked
if record exists:
if record.fingerprint != fingerprint → 409 DEFINED_FAILURE
else → return the stored outcome // do not re-execute
else:
write the "accepted" marker (key, fingerprint)
commit
run the business logic
persist the final outcome and correlationId
respond
Reconciliation is a read by key and nothing else — no writes at all.
What the check does not cover
The check verifies envelope shape, idempotency semantics, and credential enforcement. Failure paths such as retries, timeouts, and oversized results cannot be triggered from outside, so they are not covered — nine green items do not mean those paths are correct. They are contract requirements all the same, and remain yours to satisfy.
There is no offline version of the conformance check; trigger it from the connection page once your connection is submitted.
Where the protocol and this page disagree, trust the platform's actual API behaviour. For the onboarding path see Becoming a provider; for current gaps see Current limitations.