Everything the app does runs through the same API and SDK, so an agent or another client can do it too. Actions on the Writ program are prepared by the server, signed by the wallet, and carried to finality by Writ's worker.
- DraftWrit prepares the exact transaction with an identity memo. Unsigned drafts expire after 90 seconds.
- QueuedYour signed bytes are saved; one signed action per wallet is in flight at a time.
- SubmittedThe worker broadcasts the same bytes until a finalized result exists.
- FinalizedSuccess or failure on chain is final; the index catches up within seconds.
Sign in#
-
POST /api/auth/challengewith{ "wallet": "<address>" }returns a message bound to the site's origin, the wallet, the program, the chain's genesis hash, a nonce and an expiry five minutes ahead. -
Check those bindings, then sign the exact message with the wallet.
-
POST /api/auth/verifywith{ "challengeId", "wallet", "signature" }, the signature in base64. Writ sets an HttpOnly session cookie for 12 hours.
Every POST sends the site's Origin. The SDK's WritClient.authenticate(wallet, genesisHash) does all three steps and checks the message for you.
Prepare, sign, submit#
-
Prepare.
POSTthe action to its endpoint with anIdempotency-Keyof 8 to 100 letters, digits,_or-. Writ answers with an operation ID and an unsigned transaction: your action plus an SPL Memowrit:v1:<operationId>, paid by your wallet. -
Check. Rebuild the expected instructions with the SDK and compare the whole message before signing.
WritClient.signAndSubmitdoes this. -
Sign with the wallet, without changing the message.
-
Submit.
POST /api/transactions/submitwith{ "operationId", "transaction" }, the signed transaction in base64. -
Follow.
GET /api/operations/<id>until it isFINALIZEDorFAILED.
Unsigned preparations expire after 90 seconds. Retrying with the same key and body returns the same operation. A wallet has at most one signed operation in flight; preparing another returns SIGNED_OPERATION_PENDING until the first has a final result.
Actions#
| Action | Endpoint | Body |
|---|---|---|
| Create a rule set | POST /api/rules | name, nonce, clauses, maxPassAgeSeconds |
| Bind a mint | POST /api/mint-bindings | mint, rule, requireSource |
| Prove a rule set | POST /api/proofs | rule, expiresAt, proof |
| Issue | POST /api/mints/issue | mint, destination, amount |
| Transfer | POST /api/transfers | mint, source, destination, amount |
Amounts, nonces and times are decimal strings. destination and source are token account addresses.
Proving from your own client#
POST /api/proofs/context with { "rule" } returns the credential, rule set, verifier, circuit and chain time the proof must use. Build the proof with the SDK's generateEligibilityProof(buildEligibilityContext(credential, rule, expiresAt), witness, artifacts), which checks the pinned proof artifacts and verifies the proof locally before returning it. Only the proof is posted.
Reading#
GET /api/passport: your credential, passes, every rule set with its count of live passes, and bindings, from Writ's finalized index.GET /api/rules: public rule sets.GET /api/network: governance, verifiers and the circuit.GET /api/config: the program, the public RPC, the genesis hash and the proof artifact manifest.
The full list is in the HTTP API reference.