Ledger endpoints
Each call is an HTTPS POST with a JWE envelope.
Authorize places a hold; that hold then ends in capture or release.
Field-by-field claims are in the API reference: authorize, capture, release, credit account. A paid request is the other direction: credit books a deposit after money is already in your wallet.
Authorize #
Called when a user initiates a payment. You decide whether to place a hold on their funding source. Claims: authorize.
The request claims include the payout ID, your own externalID for the
sender (so you can resolve them without Moov’s participant IDs),
fundingSource (the providerSourceID you registered for the account to
hold against), the amount, and a fraudScore from 0.0 to 1.0,
where higher means riskier. You may factor the score into your decision, for
example requiring step-up authentication above a threshold. How that score
is produced, and when a payment is held for review instead, is in
trust & safety.
amount is a decimal string plus a currency descriptor. Scale by
currency.decimalPlaces into minor units; never parse value as a
float:
{ "value": "100.00", "currency": { "code": "USD", "decimalPlaces": 2 } }
Authorize may also carry callerSubject and requestID. Both are
audit-only trace values; this requestID is not a request for payment.
Outcomes on 200 OK:
| Outcome | Meaning | You must return |
|---|---|---|
approved | Hold placed | holdReference, your ledger’s reference for the hold |
denied | You declined: insufficient funds, risk rules, step-up required | reason |
Moov supplies your holdReference back to you on the later capture or
release call for the same payout.
Do not return 200 with any other outcome value to signal a processing
failure. Moov Money treats unmodeled outcomes as an internal unknown
result and discards your reason. If your ledger is unavailable, return a
retryable HTTP status (408, 429, or 5xx) instead.
Capture #
Called after the recipient completes their claim onto a debit card, Apple Pay, or Google Pay. You settle the hold, debiting the sender so you can originate the push-to-card payment. See flow of funds. Claims: capture.
The request carries the holdReference you returned on authorize. Capture
has no denied outcome. Either you captured the hold or you did not:
| Outcome | Meaning | You must return |
|---|---|---|
approved | Hold captured, funds moved | captureReference |
failed | Your ledger could not capture the hold | reason |
Two capture-specific rules:
- Already-captured holds must return
approved. A repeat capture of the sameholdReference, whether or not the idempotency key matches, must returnapproved, reusing the originalcaptureReferenceif you can look it up. - Unknown holds must return
approved. If aholdReferencehas aged out of your records entirely, still returnapproved. Moov Money treats unknown holds as already captured; the alternative would strand funds in flight. If this doesn’t fit your ledger model, escalate to Moov before implementing.
Release #
Called when a payment is canceled or its 10-day claim window expires. You release the hold, returning funds to the sender. Claims: release.
The request carries the holdReference plus a non-normative reason, with
values like cancelled (user-initiated) and expired (hold time to live exceeded)
so you can distinguish them in your ledger.
| Outcome | Meaning | You must return |
|---|---|---|
approved | Hold released | releaseReference |
failed | Your ledger could not release the hold | reason |
Releasing an already-released hold must return approved, reusing the
original releaseReference when you can.
Credit #
Called after a request has been paid and the funds are in your
wallet (the request is funded). You book a deposit to the requester. Same JWE
envelope as authorize. Claims:
credit account.
The request carries requestID (the request-for-payment, not a payout
ID), requesterExternalID, fundingDestination (opaque token for
where the deposit lands), the amount, and an idempotencyKey. Deduplicate
on that key so a retry never double-posts. The key’s scope is one
disbursement attempt.
Outcomes on 200 OK:
| Outcome | Meaning | You must return |
|---|---|---|
approved | Deposit posted | creditReference, your ledger’s reference for the credit |
destination_unavailable | This destination cannot receive it (closed or invalid account). Retryable. | reason |
refused | You refuse the deposit itself. Terminal; Moov begins reversal. | reason |
Do not return 200 with unknown or any other unmodeled value.
Moov synthesizes unknown for timeouts and unrecognized outcomes; if
your ledger is down, return 408, 429, or a 5xx so Moov retries
the same idempotencyKey. Other 4xx are terminal.
Repeating an approved credit with the same key must return approved
and the original creditReference.
Card-funded programs skip this callback: the requester is credited by an OCT to their FI-issued card instead. See flow of funds.
Every response, every time #
Regardless of endpoint or outcome:
- Echo the request’s
idempotencyKey(undermoov) exactly. Moov rejects the response if it doesn’t match. - Omit the reference field (
holdReference,captureReference,releaseReference,creditReference) on any non-approvedoutcome. - Put
iatandexpat the top of the response JWT, not insidemoov. - Include a human-readable
reasonfor any non-approvedoutcome (denied,failed,destination_unavailable,refused).
Next steps #
- Idempotency and retries: the delivery guarantees behind these rules.
- API reference: every claim, with examples. Start with credit account if you collect on requests.