POST /api/v1/webhook/sync accepts reconciliation events from an issuer platform. It is the only
endpoint authenticated with an issuer API key rather than a bearer token, and it requires three
headers on every request.
The three headers
Signature
The signed string is the nonce, a literal., then the exact bytes you send as the body — not
a re-serialised copy. Serialise your payload once, sign that string, and send that same string.
Re-encoding between signing and sending is the most common cause of a rejected signature.
The server compares in constant time.
Nonce
x-nonce is rejected if it is more than five minutes from server time in either direction, and
rejected if the same key has already used that value. Every request needs a fresh nonce —
including retries. Reusing the nonce from a failed attempt will fail again as a replay.
Key binding
Your key must belong to the issuer named indata.issuer._id, or be unclaimed. The first
correctly signed call binds an unclaimed key to that issuer permanently. Send your first request
carefully; the binding cannot be undone through the API.
apiSecret is returned only in the response that creates the key. It is not retrievable
afterwards — store it when you create it.Signing a request
Troubleshooting a 401
Signature does not match
Signature does not match
Almost always a body mismatch. Confirm you signed the exact string you sent — no pretty-printing
on one side, no key reordering, no re-serialisation by your HTTP client. In Python, pass
data=
rather than json=, or the client will re-encode the payload.Nonce rejected as stale
Nonce rejected as stale
Your clock has drifted more than five minutes from ours. Sync against NTP.
Nonce rejected as reused
Nonce rejected as reused
You retried with the nonce from the previous attempt. Generate a fresh one for every request,
including retries of a failed one.
Key rejected
Key rejected
Either the key is inactive, or it is already bound to a different issuer than the one in
data.issuer._id.
