Skip to content
YutaiDocs
GET /v1/statusGet API key

Integrate / Sign a balance statement

Sign a balance statement

How a custodial distributor signs the balance statement Yutai stores as its attestation: the two kinds of statement, what one payload carries, the exact message to sign, how fresh a statement must be and the signature Yutai verifies. The formats every value travels in are the identifiers page's: chains, instruments, quantities, instants and the prefixed ids.

View as .md

Yutai does not take statements from real distributors yet. The statements it records run on simulated data, so nothing a real distributor sends reaches it; the rules on this page are the ones its stored statements already follow.

Kinds of statement

Yutai learns your users' tokenized holdings from statements your distributor signs and sends. Every statement is one kind. The kinds Yutai's registry names:

Push push
A statement your system sends as the account's positions change: it carries the current position of every instrument it names.
Snapshot snapshot
The account's full statement: it names every instrument the account holds, so anything a push missed is caught.

What one payload carries

One statement carries the facts it binds, under the bounds the contract fixes:

tenantId
The Yutai member the statement is about: the one whose holder the account belongs to. It is the member's own prefixed id, the identifiers page's form.
venueId
The Yutai member your distributor runs under: the one whose registered key verifies your signature.
accountRef
Your distributor's own reference for the account, the one its link to the holder carries. It carries 1 to 128 characters of letters, digits, dot, underscore, colon and hyphen; a statement whose reference carries anything else is refused.
payloadId
The statement's own id, yours to mint. It carries 1 to 128 characters of letters, digits, dot, underscore, colon and hyphen. One statement per id: a second delivery of the same id with the same signature answers the first statement and writes nothing, and an id sent again with other content is refused.
kind
One of the two kinds above.
attestedAt
The instant your system attested the statement: ISO 8601 in UTC, the identifiers page's instants.
positions
The current position of every instrument the statement names, one line each: the instrument's id, the one the instrument listing carries, and the exact base units you hold, as one whole number. At most 64 lines, each instrument once.
movements
The account's history, one line per movement. At most 256 lines; a longer history arrives across several statements. Each movement carries its own id (yours, once across your accounts), its kind (the four below), the instrument, the base units it moved and the instant it happened at. No movement happens after the instant the statement attests.

One movement carries:

Buy buy
Tokens moved in on a purchase at your distributor. No wallet is named.
Sell sell
Tokens moved out on a sale at your distributor. No wallet is named.
Transfer in transfer_in
Tokens moved in from a wallet: the wallet they came from is named. It may also name the on-chain transaction that carried them.
Transfer out transfer_out
Tokens moved out to a wallet: the wallet they went to is named. It may also name the on-chain transaction that carried them.

The message

You sign the statement's own message, built from the payload's facts in one fixed order: the header lines, then one Position line per position and one Movement line per movement, in the payload's own order. Instants are ISO 8601 in UTC and amounts are the exact base-unit digits, so the signer and the verifier build byte-identical text. The message over this page's sample payload:

The message the signature binds
Yutai venue attestation
Tenant: tnt_01J9ZR2C4E6G8H0J2K4M6P8ZAB
Venue: tnt_01J9ZR3D5F7H9J1K3M5P7Z9BCD
Account: acct-4177
Payload: stmt-1001
Kind: snapshot
Attested: 2026-10-09T04:00:00.000Z
Position: tki_01J9ZR4E6G8H0J2K4M6P8ZACEX 1250000
Position: tki_01J9ZR5F7H9J1K3M5P7Z9BDFFA 300
Movement: mov-0091 buy tki_01J9ZR4E6G8H0J2K4M6P8ZACEX 250000 2026-10-08T15:12:00.000Z
Movement: mov-0092 transfer_in tki_01J9ZR5F7H9J1K3M5P7Z9BDFFA 100 2026-10-08T18:40:00.000Z 4Nd1mBQ6MJ6ViN5jM2Z4atPozHvGvQtME5hkmZjpSXRV

How fresh a statement must be

The freshness window
Yutai accepts a statement at most 300 after the instant it attests. An older one is refused, however it was signed.
The clock skew
Your attested instant may sit at most 60 ahead of the instant Yutai receives the statement. Further ahead is refused.
The hourly cap
Yutai accepts at most 60 per account per hour, so a backend stuck in a retry loop cannot flood one account. The same statement delivered again inside that hour is answered as the first one and does not count twice.

The signature

You sign the statement's message with the signing half of the key your distributor registered. Yutai verifies every statement against that registered public key before anything is stored; a statement that does not verify is refused and audited, and nothing of it is stored.

The scheme
Ed25519, the one scheme the registry carries: a public-key scheme, so Yutai verifies with your registered public key and never holds a signing half.
The key
The public half of your signing key: 43 characters of letters, digits, underscore and hyphen, the unpadded base64url of 32 bytes. Only the public half is stored; the signing half stays with you.
The signature
The signature of the message above: 86 characters of letters, digits, underscore and hyphen, the unpadded base64url of 64 bytes.
The key id
Your statement may name the key that signed it. When it names none, Yutai verifies against your distributor's first key, k1. A key id carries 1 to 32 characters of letters, digits, dot, underscore and hyphen.
Replacing the key
When you replace the signing key, the new one starts at the instant you name and the old one keeps verifying until the instant you name too: at most 72 hours after the new one starts. Both verify side by side during that overlap, so a statement signed just before the switch still lands.

The one scheme the registry carries: ed25519. A second scheme extends the registry's list; a caller's own string is refused.

What Yutai refuses

Every refusal below answers with nothing stored: the statement leaves no trail, and the refusal itself is the only record of it.

No signature
A statement that carries no signature is refused.
No registered key
A statement from a distributor whose key is not registered is refused.
A signature that does not verify
A signature that does not verify over the message above, under the registered key, is refused.
The account reference
A reference outside its own rule, the one the field above carries, is refused.
The account's link
The account must carry an active link on the statement's member, made no later than the instant the statement attests. A statement about an account with none, or attested before its link took effect, is refused.
The distributor's scope
Every instrument the statement names must be one your distributor's own listing carries. A statement naming any instrument outside it is refused whole.
The registry
Every instrument must be one the statement's member's registry holds as active. One the registry has paused or retired refuses the whole statement.