Account and access management API
Management routes configure the organization, its Safe, API keys, server signer, and named agents. Except for the three /studios/me reads and the public enquiry endpoint below, these routes require a Privy JWT and the appropriate owner context. API keys cannot manage themselves, their signer, or agents.
Use the getting-started flow for onboarding, API-key guide for credentials, and agent guide for integration lifecycle decisions.
User and organization reads#
| Endpoint | Authentication and input | Success envelope |
|---|---|---|
POST /v1/auth/login | Privy JWT; synchronizes or creates the user record | { success, user }; user includes studioId and isNewUser |
GET /v1/auth/me | Privy JWT | { success, user } |
GET /v1/studios/me | HMAC dashboard:read or JWT | { data: { id, name, safeAddress, safeChainId, safeDeploymentStatus, createdAt } } |
GET /v1/studios/me/balance | HMAC dashboard:read or JWT | Top-level success, balance, formatted, symbol, decimals, and deployed Safe/network fields |
GET /v1/studios/me/invoices/stats | HMAC dashboard:read or JWT | { success, stats } |
GET /v1/studios/:id | Owner JWT; studio UUID | { success, studio } |
GET /v1/studios/:id/balance | Owner JWT | Top-level balance fields |
GET /v1/studios/:id/invoices/stats | Owner JWT | { success, stats } |
GET /v1/studios/:id/transactions | Owner JWT; page starts at 1, limit defaults to 20 and is clamped to 1 through 100 | { success, transactions, pagination, safeAddress?, chainId? } |
GET /v1/studios/:id/quotas | Owner JWT | { success, studioId, quotas } |
Balances and statistics amounts are raw USDC integer strings; formatted is a display amount. Statistics include totalCount, pendingCount, paidCount, canceledCount, totalPaidAmount, and totalPendingAmount. Pending here means invoices still Issued. These statistics effectively read at most 50 invoices per status without pagination, so they are not an unlimited historical ledger. Balance reads can return zero plus a setup note before Safe deployment.
Transaction pagination includes page, limit, total, totalPages, hasNext, and hasPrev. This is Safe transaction-service activity, not a complete Cleared payment export. Upstream failures can produce an empty successful list; an empty result alone does not establish no activity. Transaction native-token value is not a USDC amount.
Quota entries expose daily/monthly limits and usage, reserved operations, remaining counts, and ISO reset dates. Read the returned values rather than hardcoding a plan limit. Quotas are distinct from HTTP rate limits.
Business profile and withdrawal destination#
| Endpoint | Request | Success envelope |
|---|---|---|
GET /v1/studios/:id/profile | Owner JWT | { success, profile, profileComplete } |
PUT /v1/studios/:id/profile | Complete business profile | { success, studio: { id, name, displayName, profile, profileComplete } } |
GET /v1/studios/:id/withdrawal-address | Owner JWT | { success, address, updatedAt } |
PUT /v1/studios/:id/withdrawal-address | { address } | { success, address, updatedAt } |
A profile requires type: "entity" | "person", legalName, valid email, and address: { street, city, postal, country }. Entity profiles additionally require registrationNumber and optionally accept vatNumber and tradeName. Person profiles optionally accept taxId and vatNumber. Country must be a valid ISO code. PUT replaces the profile; it is not a partial PATCH.
Changing the withdrawal address changes where subsequent withdrawals may be prepared/submitted. The address must be a valid nonzero EVM address other than the studio's own Safe. This management action does not itself move funds. See withdrawals.
Safe provisioning and upgrade#
These are owner-controlled transaction flows, not ordinary backend API-key operations. A prepare response has not deployed or upgraded anything. Sign only after reviewing the operation and incorporate the returned final sponsorship fields before signing.
| Endpoint | Request | Result |
|---|---|---|
POST /v1/studios/prepare | { name?, chainId, userOp }, unsigned deployment UserOp with optional stubSignature | { success, sponsorship } with paymaster and gas fields |
POST /v1/studios/submit | { name?, chainId, userOp }, signed and sponsored | Top-level success, studioId, safeAddress, userOpHash, status, and txHash on receipt; may return 202 pending |
POST /v1/studios | { name?, chainId, userOp }, existing signed deployment route | { success, studio, message }; deployment may still be pending. Prefer the explicit prepare/submit flow. |
POST /v1/studios/:studioId/upgrade-7579 | Owner JWT | { already7579: true } or { already7579: false, userOp } |
POST /v1/studios/:studioId/upgrade-7579/submit | { signed_user_op } | { success, txHash, userOpHash } |
chainId is an integer supported by the deployment. The name is optional/nullable, up to 100 characters. A v0.7 UserOp contains sender, nonce, calldata, gas and fee fields, optional deployment factory fields, and the appropriate signing/sponsorship fields. Use a Safe-aware client rather than constructing arbitrary calldata from this table. Upgrade preparation may reconcile an already-upgraded Safe's stored state.
API keys#
Base path: /v1/studios/:studioId/api-keys. All require the studio owner's JWT. Do not include x-cleared-key.
| Method and suffix | Request/filter | Success response |
| ----------------------- | ---------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------- | ------------------------------ |
| POST / | { name, scopes, expires_in_days? } | 201 top-level key_id, one-time secret, name, scopes, secret_hint, write_status, created_at, activation_required, optional activation |
| GET / | Optional status=active | pending_activation | revoked filters write status | { keys, signer }; no secrets |
| DELETE /:keyId | Public key ID | { key_id, status: "revoked" } |
| POST /:keyId/rotate | Public key ID | { key_id, secret, secret_hint }, new secret shown once |
| POST /activate-signer | { signed_user_op } from owner-signed activation | { signer_id, status: "active", tx_hash } |
| GET /audit-log | limit 1 through 100 (default 50), optional UUID cursor | { events, next_cursor } |
Key names are 1 through 100 characters. scopes is a nonempty array of supported scope strings; see authentication. expires_in_days is an integer 1 through 365, or null/omitted for no requested expiry. Write-key validity is bounded by the signer's session expiry. Revocation stops HTTP authentication; rotation immediately invalidates the old secret, without changing the key ID.
Creating write access can return activation_required: true and activation: { signer_address, user_op }. The key is not ready for server-signed writes until activation succeeds. The owner signs the returned operation and submits it using the snake-case signed_user_op field, not userOp. Preserve all returned fields.
status on listed keys reflects revocation, while write_status describes write readiness. Neither label alone proves the credential has not expired. The HMAC authentication check rejects expired keys.
Server signer lifecycle#
Base path: /v1/studios/:studioId/signer. All require owner JWT.
| Endpoint | Effect and response |
|---|---|
GET /status | Returns status, optionally signer IDs/address and transaction data. Pending status may include a fresh activation.user_op or activation_error. upgrade_required includes safe_address. |
POST /revoke | Prepares on-chain session removal, returning { signer_id, status: "pending_revocation", user_op }. Does not by itself complete on-chain revocation. |
POST /confirm-revocation | Accepts { signed_user_op }; submits the owner-signed removal. Returns { signer_id, status: "revoked", tx_hash } after successful receipt. |
POST /force-revoke | Database-only revocation and disabling associated write access. Returns { signer_id, status: "revoked", message }. Leaves the on-chain session installed. |
The signer is shared by keys for the studio/chain. Revoking it affects associated write access, not just one integration. Revoking or rotating an individual API key is a different action. A timeout in a lifecycle submission requires receipt/state reconciliation, not an assumption that nothing happened.
Agents and linked keys#
Base path: /v1/studios/:studioId/agents. Owner JWT is required for every route. A named agent is a management record for an integration, not a wallet signer.
| Method and suffix | Input | Success response |
|---|---|---|
POST / | { name, description?, icon?, scopes, expires_in_days? } | 201 { agent, api_key, activation_required, activation? } |
GET / | No pagination parameters | { agents, signer } |
GET /:agentId | Agent ID | Agent object directly |
PATCH /:agentId | { name?, description?, icon?, status? } | { agent } |
DELETE /:agentId | Agent ID | { deleted: true, keys_revoked } |
POST /:agentId/keys | { scopes, expires_in_days? } | 201 { api_key, activation_required, activation? } |
GET /:agentId/keys | Agent ID | { keys }, no secrets |
POST /:agentId/keys/:keyId | Existing key ID | { success: true }, links the key |
DELETE /:agentId/keys/:keyId | Existing key ID | { success: true }, unlinks the key only |
Maximum 50 agents per studio. Names are 1 through 64 characters; descriptions are nullable, at most 256. Icons: bot (default), code, server, database, cloud, terminal, webhook, plug, cpu, globe. Expiry has the same 1 through 365 day/null rules as keys. PATCH status is active or disabled; it does not edit key scopes.
Disabling an agent rejects HMAC requests made with linked keys. Deleting an agent revokes linked keys. Unlinking is not revocation: the key retains its secret and may continue authenticating independently. Revoke the key itself to terminate credential access. API-key use activity is sampled at about 10%, with asynchronous logging, so the audit-log endpoint is not a complete request ledger.
Public enterprise enquiries#
POST /v1/leads is an unauthenticated, rate-limited marketing form endpoint. It creates an enquiry and can send notification/confirmation emails; it does not provision an enterprise account or configure fees. Success is 201 { data: { id } }.
Required strings: firstName and lastName (1 through 100), email (valid, at most 254), company (1 through 200), role (1 through 100), useCaseDescription (10 through 2000). Required enums: platformType (dsp, igaming, gaming, financial, other), monthlyVolume (under_100k, 100k_500k, 500k_2m, 2m_10m, 10m_plus), and timeline (exploring, 1_3_months, asap). Optional strings: referralSource and sourcePage (maximum 200), additionalNotes (2000), sourcePlan (100). Use the enterprise form for an ordinary enquiry.