bijection/invoices.ts
billing is an integration whose setStatus
command describes how to send the request and how to tell whether it took
effect. billing_invoices is the table that integration keeps in sync.
Submitting an external call
ctx.externalCalls.submit(command, options) takes a command from one of your
integrations and these options:
It returns the call’s ID, a string you can store and use to follow the call.
Submission is part of the transaction:
- The call is recorded only if the transaction commits. If preparation throws after submitting, the call disappears with the rest of the transaction.
- The call can only be submitted from a mutation transaction, such as an
operation’s
prepare. Anywhere else it is refused withMutationRequired. - The command’s arguments are fixed when the request is accepted. Later retries send those same arguments; they are never recalculated by newer code.
Submitting records your intent. It does not update the synced table. The
table changes only when the external system’s answer confirms the change and
the confirmed record is published back.
The lifecycle of an external call
1
Persisted
The call commits with the operation, before any request is sent. From this
point the engine owns delivering it, independently of the process that
submitted it.
2
Held and sent
When the call’s turn comes, the engine holds it and sends the command’s
request. A call can wait here for an earlier call it is ordered after, or
for an approval.
3
Outcome recorded
The external system’s response is retained and interpreted under the
command’s declared evidence contract. The resulting outcome is recorded in
its own commit.
4
Published
Confirmed facts reach the synced table, where your queries see them: from
the response itself when it proves the resulting record, otherwise from a
later read of the source. Confirmation and publication are recorded
separately.
Delivery outcomes
Each external call has one of these delivery states, reported by an operation’s status:
A call’s status also reports its
publication: null, pending, blocked, or
published at a revision.
Delivery guarantees
What the engine may do after a lost response depends on the command’s declared delivery contract, which the integration states for each command:- idempotent
- single_attempt
The destination deduplicates requests by the call’s identity for a stated
period. Within that period, the engine can send again under the same
identity, and the destination treats it as the same request. If the
destination states no retention period, the engine never resends under the
retained identity.
delivered.
Unknown outcomes and recovery
A timeout, a crashed worker, or a missing response does not prove that a call failed. The engine records such a call asunknown, and it stays unknown
until it is reconciled under the command’s own contract, for example by reading
the destination’s records for this call’s identity.
When you meet an unknown outcome:
- Do not submit a replacement. A new request is a new business change and could apply the effect twice. The original call keeps its identity.
- Recover the same call. A deployment administrator can inspect and recover it from the CLI. Recovery reuses the accepted identity, interprets retained evidence or performs a bounded read, and never sends a new write.
command-status reports the retained outcome, the stopping reason, the attempt
and byte budgets, and the evidence still required. See also
CLI and console.
Reading a call’s status in your code
externalCallStatus(ctx, id) returns the delivery state of a call your
component submitted, as one of the states above. It is an ordinary tracked read,
so a query that calls it re-runs when the call settles.
bijection/invoices.ts
ExternalCallNotFound.
Nothing else about the call is disclosed: not its arguments, its attempts, nor
the destination’s own words for a refusal.