Approvals & signing
Every write that moves funds — a payment, a trade, an Earn deposit, a Borrow draw — shares one lifecycle, and your integration should be built around it rather than assuming a synchronous result. Mooneum is non-custodial with maker-checker controls: the API can prepare and request, but execution waits for policy, approvers, and a signature from the org's own keys.
The lifecycle
- Create — your API call creates a draft order (
POST /api/v1/trade-orders,/earn-orders,/borrow-orders, or a payment). - Submit —
POST …/{id}/submitsends it into the pipeline; policy rules evaluate (limits, whitelists, pair/protocol restrictions). - Approval — if a threshold requires sign-off, the order waits. Approvers act in-app, or
programmatically:
GET /api/v1/approvalslists pending items andPOST /api/v1/approvals/{domain}/{id}/deciderecords an APPROVE/REJECT (client.decide(id, 'APPROVE')). - Signing — the transaction is signed by the org's wallet or a paired Mooneum Signer
device. A
Wallet.SigningRequestedevent fires when a signature is being waited on. - Broadcast & confirm — the signed transaction publishes on-chain and confirms.
The events to listen for
All four verticals publish one shared event stream, with a domain field
(payment | trade | earn | borrow):
Order.Created → Order.Submitted → [Order.Approved | Order.Rejected]
→ Wallet.SigningRequested → Wallet.TransactionSigned
→ Order.Broadcast → [Order.Confirmed | Order.Failed]Subscribe via webhooks or WebSockets; the
complete catalogue with payload fields is in the events reference.
Multi-leg orders (an Earn withdrawal with a CLAIM leg, an approval-then-swap) carry a leg
field so you can tell which step just happened.
Design for 'pending', not 'done'
A 2xx from submit means "accepted into the pipeline," not "executed." Persist the order id,
treat Order.Confirmed (or Order.Failed / Order.Rejected) as the terminal signal, and use
idempotency keys on create calls so retries never double-submit.
What this means for common integrations
- A payout job creates + submits payments, then reconciles on
Order.Confirmed— it never needs (and shouldn't have) approval or signing permissions of its own. - An approvals bot (e.g. posting to Slack) reads
GET /api/v1/approvalsand callsdecide()after a human clicks — the API key needsApproveon just that product. - A treasury dashboard is read-only: portfolio, positions, audit logs. No part of this lifecycle applies to reads.
Scopes for all of this are per product and per action — see the permissions reference.