Push
Write an already-signed attestation onto a target chain's own storage.
POST /v1/push
This endpoint writes an already-signed attestation onto a target chain's own storage, so other parties and their contracts can read it locally without calling Attestra. It is the only endpoint in this API that sends a blockchain transaction, and it is the only one that costs gas.
Request body
{
"target": "ethr",
"attestation": { "...": "as returned by Verify or Verify quorum" },
"signatures": ["0x...", "0x..."]
}| Field | Type | Required | Description |
|---|---|---|---|
target | string | yes | "ethr", "eid", or "fabric". The chain to write to. |
attestation | object | yes | The signed attestation to write. |
signatures | array of strings | yes | One or more signatures over the attestation's digest, ordered as described below. |
signatures is an array, not a single value, because on-chain verification
counts distinct authorized signers against a threshold; see
the quorum rule. A legacy single signature
string is still accepted and relayed as a one-element bundle. This is not a
bypass: a one-signature bundle still has to clear the target's on-chain
quorum check like any other bundle, and it only succeeds if that target's
threshold is genuinely 1. When the threshold is higher, a one-element bundle
is rejected on chain, which is the correct outcome, not a special case.
Signature ordering
Signatures must be ordered by recovered signer address, ascending. The target chain re-verifies each signature itself, recovers its signer, and requires each recovered address to be strictly greater than the last: this is what proves the signers are distinct and lets the chain count them in a single pass. See Verifying for why the ordering is required and what it defends against.
Verify quorum already returns its
signatures array in this order, so the usual flow is to pass that array
through to push unchanged, without re-sorting it.
Trust rests on the signatures, not the sender
Anyone may submit the push request: a relayer, the holder, or any other
party who has the signed attestation and its signature bundle. The target
chain verifies the signatures on chain and does not check who called this
endpoint or who broadcast the resulting transaction. Possession of a quorum
of valid signatures is what authorizes the write, not the identity of the
caller.
Response
{
"ref": "0x...",
"target": "ethr"
}ref is chain-specific. On Ethereum it is the transaction hash.
On Hyperledger Fabric the pusher returns the literal string committed,
because Fabric's submit call commits before it returns, so there is no
separate hash to report.
A 502 does not mean nothing happened
This is the most important thing on this page.
push broadcasts the transaction and then waits to see it mined. If that
wait fails, or the transaction is mined but reverts, the endpoint returns a
bare 502. That 502 does not mean the write did not happen. Once a
transaction is broadcast, everything after that point is a report on a real
transaction, not a decision about whether to send one. A 502 can mean the
transaction was broadcast and is still pending, or that it was broadcast and
reverted. It never means the request was a no-op.
The error message carries a clue, but only sometimes. If the transaction was
broadcast and the failure happened while waiting for it to be mined, the
transaction hash is embedded in the message itself, in the form
wait mined (tx 0x...); the hash is kept there because that is what an
operator needs in order to look the transaction up. If the transaction was
mined and then reverted, there is no hash in the message: the message says
only that the push reverted.
The correct recovery is not to retry blindly. Retrying a push that actually landed produces a second, redundant transaction and spends gas twice. Instead, read the attestation back with Attestation and decide from what that read returns.
This has a real limitation worth stating plainly: read-back is implemented
on Hyperledger Fabric only. On Ethereum, GET /v1/attestation
returns 501 regardless of what happened to the push, so it cannot be used
to resolve a push failure there. On Ethereum, the only route after a 502
is the transaction hash recovered from the error message, checked against a
block explorer. On Hyperledger Fabric, read the attestation back directly
and let that answer settle it.
Status codes
| Code | Meaning |
|---|---|
| 200 | The write succeeded. ref identifies the transaction or commit. |
| 400 | Malformed request body, invalid signature hex, or no signatures supplied. |
| 404 | No pusher is registered for the requested target. |
| 502 | The write failed, or its outcome could not be confirmed. See above: this does not mean nothing happened. |
Request bodies are capped at 1 MiB.