Build logEngineering

What 204 No Content proves—and what it does not

A successful calls/end response confirms the interaction is already committed durably. Enrichment and other background work follow their own timing.


POST /v1/calls/end returns 204 No Content on an accepted request. That proves the interaction is already committed durably: the database write happens as part of the request, and a recall that starts afterward normally returns it. It does not prove that every other downstream job (enrichment, other background work) has completed, or that a client should parse JSON.

Acknowledgement and observation are different receipts

For Northsun's caller-memory path, acknowledgement and the durable write happen in the same request: the interaction is committed durably before calls/end responds 204. When the next recall will show it is a separate question. A recall that was already in flight when the call ended, or a failed refresh of the recall cache, can serve the earlier state until that cached view is refreshed. Enrichment and other background work follow their own timing.

The quickstart therefore still asks for a later recall with a new session ID, to prove the behavior against your own integration rather than trust the claim. Put a unique phrase in the first user turn of the synthetic transcript: the later recall shows each recent call's summary as that first user turn, in memory.recent_calls, so the phrase should appear there on that read (the top-level memory.summary describes the caller record, not this call). If it does not on the first try, read again briefly before treating it as a failure, and keep both trace IDs. See Hangup, and what happens next for the contract.

Do not parse a body

This is wrong:

ts
const response = await fetch(url, options);
const receipt = await response.json();

A 204 has no response body. Branch on status and preserve correlation data instead:

ts
const response = await fetch(url, options);
if (response.status !== 204) {
  throw await toNorthsunError(response);
}

return {
  acknowledgedAt: new Date().toISOString(),
  sessionId: redactedSessionId,
};

That local receipt reflects only what your client observed synchronously. The interaction itself is already committed durably by the time 204 returns — label the receipt "acknowledged," and treat a later read as verification of your own integration, not of the API's guarantee.

Retrying an uncertain write

Voice platforms retry webhooks, networks fail after sending bytes, and clients can lose the response after the server handled the request. Use the platform’s stable call ID to deduplicate in your application. Avoid blindly posting the same completed call in an unbounded loop.

When the result is uncertain:

  • retain the UTC time, redacted call/session ID, endpoint, and request/trace headers;
  • inspect a later read before creating another write;
  • retry only under a bounded policy appropriate to your idempotency design;
  • escalate with the minimal safe diagnostic receipt if state remains ambiguous.

The proof ladder

Name exactly which rung you reached:

  • Request serialized locally.
  • Server returned 204.
  • A later recall exposed the controlled interaction.
  • The exact voice-platform webhook path produced the same result.
  • The behavior survived duplicate delivery, missing enrichment, and a temporary failure.

Only the final rungs support a production lifecycle claim for that configuration.

Run the Quickstart, inspect API reference, and use Troubleshooting when the acknowledgement and later read disagree.