Skip to Content
API ReferenceBridge API

Bridge API

Solidus Bridge carries the status of a Solidus credential to the chains a holder chose, so an application there can check a condition without seeing the data behind it. On the testnets it also gates a USDC transfer on a credential: only an address that holds one can receive it. The API reads what the bridge observed. It does not sign, and it does not move anything.

Base URL: https://bridge.solidus.network

Network: testnet. The /v1/routes response says "network": "testnet". Everything here runs on testnets and has no monetary value.


Conventions

  • Read only. Every route is a GET. Anyone may call them. The one exception is GET /v1/me/exports, which needs a Solidus session token (Authorization: Bearer ...).
  • null means the bridge did not measure it. It never means zero and it never means healthy. A heartbeat age of null is a chain the bridge has heard nothing from.
  • signed, delivered and applied are observation times. signed records when the bridge first saw the signature threshold reached, so it is bounded below by the polling interval. delivered and applied record when the gateway first saw the applied event, after the destination block is final by that chain’s rule, so they trail the block by the finality wait. Do not compute a signing or delivery latency from them without that wait. queued and committed are Solidus chain times.
  • Delivery and application are the same transaction, so delivered and applied are equal.
  • Anyone may deliver a message. The signatures are the authority, not the sender. Read them from GET /v1/attestations/{domain}/{seq} and submit them to the destination mirror. The message bytes come from the Solidus RPC method solidus_getBridgeMessages ({"domain", "fromSeq", "limit"} at https://rpc.solidus.network). Their keccak256 hash is the messageId.
  • Errors are application/problem+json with type, title, status, code and detail.
curl https://bridge.solidus.network/v1/status

Routes

GET /health

Answers {"status":"ok"} when the API process is up. It says nothing about the chains.

GET /v1/routes

The chains the bridge is configured for, read from the live configuration.

{ "network": "testnet", "solidusDomain": 50002, "chains": [ { "key": "sepolia", "name": "Ethereum Sepolia", "vm": "evm", "domain": 11155111, "chainId": 11155111, "explorer": "https://sepolia.etherscan.io", "functions": ["credentials", "usdc"], "contracts": { "mirror": "0x...", "registry": "0x..." } } ] }

functions lists what each chain supports. contracts lists the deployed addresses for that chain.

GET /v1/status

What the bridge observed, per chain.

FieldMeaning
solidusWhether the Solidus RPC is reachable, the latest committed height and when it was checked
validatorssigning of total validators, and the threshold a message needs
chains[].heartbeatAgeSecsSeconds since the bridge first saw the chain’s latest heartbeat applied, an observation made after the destination block is final. It is not the age a gate measures on chain (a gate adds its own maxAge to the heartbeat’s timestamp and compares with the block time). null if it has seen none
chains[].nextDomainSeqThe sequence number the next message for this chain will carry
chains[].sequenceGapHow many messages for this chain are queued but not yet applied
supplyEscrow, synthetic and in-flight totals, their difference, an alert field and when it was checkedAt

GET /v1/stats?range=24h

Counts over a window. range is 24h, 7d, 30d or all. An unknown range answers over 24h and says so in the range field.

Fields: range, exports, messagesDelivered, medianDeliverySecs, sampleSize and a byChain array. medianDeliverySecs is null when there is no sample, never 0, and sampleSize says how many deliveries it covers.

GET /v1/messages?limit=10

The newest messages first. limit is clamped to 1 to 50 and defaults to 10.

{ "messages": [ { "messageId": "0x...", "domain": 1399811151, "chainKey": "solana", "domainSeq": 1035, "kind": "heartbeat", "solidusHeight": 5655587, "state": "applied", "timestamps": { "queued": "...", "committed": "...", "signed": "...", "delivered": "...", "applied": "..." }, "deliveryTx": "0x..." } ] }

GET /v1/messages/{id}

One message by messageId. 404 if the bridge has not seen it.

GET /v1/attestations/{domain}/{seq}

The validator signatures for one message, ready to submit to a destination.

{ "domain": 1399811151, "domainSeq": 1035, "messageId": "0x...", "solidusHeight": 5655587, "threshold": 3, "signatures": [ { "signer": "0x...", "signature": "0x..." } ] }

domain and seq are integers, or the route answers 400. 404 when no signatures exist yet.

GET /v1/credentials/{exportId}

The status of one credential export as the bridge last read it from Solidus: exportId, domain, chainKey, holder, issuerDidHash, credentialTypeHash, status (active, revoked, expired or unbound), validUntil, lastMessageId and updatedAt. It follows the newest message queued on Solidus, so it can say revoked before the destination applies it. To see whether the destination has, look up the message named by lastMessageId with GET /v1/messages/{id}. 404 with code: export_not_found for an unknown id.

GET /v1/me/exports

The exports of the caller’s credentials. Needs Authorization: Bearer <Solidus session token>.

GET /v1/quotes

A quote for a gated USDC transfer on the testnets. Query parameters:

ParameterMeaning
route<fromKey>:<toKey>, two different chain keys from /v1/routes, for example sepolia:fuji
tokenusdc
amountA positive integer in base units (6 decimals, so 1000000 is 1 USDC)
fastFilltrue to ask for a fast fill quote (optional)
{ "lines": [ { "id": "protocol_fee", "amount": "0", "token": "USDC", "reason": null }, { "id": "circle_fee", "amount": "100", "token": "USDC", "reason": null }, { "id": "destination_gas", "amount": "62650400", "token": "AVAX", "reason": null } ], "receive": "999900", "fastFillAvailable": false, "estimatedSecs": null, "sampleSize": 2 }

Every fee is its own line. A line the bridge cannot compute has amount: null and a reason, and receive is null whenever any line that reduces it is null, so a total is never built on a guess. estimatedSecs is null until enough comparable transfers exist.

GET /v1/transfers?address=...

The USDC transfers for one address: an EVM address or a 32-byte holder. address is required, or the route answers 400 with code: bad_address.

GET /v1/transfers/{id}

One transfer. The id is <sourceTx>:<logIndex>, with the colon URL-encoded. Fields include kind, originDomain, destinationDomain, sender, recipient, amount, fee, state, sourceTx, destinationTx, messageHash and timestamps.

GET /v1/escrows/{hash}

The escrow of a gated USDC transfer, held on the destination until the recipient claims it or it is refunded.

FieldMeaning
hash, domain, chainKeyWhich escrow, and on which chain
finalRecipient, refundAddressWho can claim it, and where a refund goes (null if none)
amount, deadlineThe amount in base units, and the deadline
credentialTypeHash, issuerDidHashThe credential the escrow names, or null until it does
stateheld, released or refunded
gateReasonThe reason the destination gate would give right now: 1 no export, 2 not active, 3 issuer not accredited, 4 expired, 5 stale. null when the escrow does not name a credential yet
Last updated on