Arca/Documentation
Join Waitlist

Scoped Tokens & Permissions

Arca uses an IAM-style, resource/capabilities permission model inspired by AWS. Both scoped JWT tokens (for end-users) and scoped API keys (for services) carry policy statements that grant specific actions on specific Arca path patterns.

Policy Model

A scope contains one or more policy statements. Each statement has an effect ("Allow" or "Deny"), a set of actions, and a set of resources (Arca object path patterns):

json
{
"statements": [
{
"effect": "Allow",
"actions": ["arca:TransferFrom", "arca:ReceiveTo"],
"resources": ["/users/u123/*"]
},
{
"effect": "Allow",
"actions": ["arca:Read"],
"resources": ["*"]
},
{
"effect": "Deny",
"actions": ["arca:*"],
"resources": ["/_internal/*"]
}
]
}

Anything not explicitly allowed is denied by default. Deny statements act as safety rails — they override any Allow statements. Use them to protect sensitive paths even when a broad Allow is in effect.

Action Catalog

Retrieve the full catalog programmatically via GET /api/v1/permissions (no auth required).

Object Lifecycle

ActionDescriptionChecked against
arca:CreateObjectCreate a new Arca objectPath being created
arca:UpdateObjectUpdate an Arca object (e.g., labels)Object being updated
arca:DeleteObjectDelete an Arca objectObject being deleted

Balance (directional)

ActionDescriptionChecked against
arca:TransferFromDebit funds (source side)Source path
arca:ReceiveToReceive funds (deposit, transfer credit, or sweep)Target path
arca:WithdrawFromInitiate outbound withdrawalSource path

Read / Observe

ActionDescription
arca:ReadObjectView object metadata and status
arca:ReadBalanceView object balances
arca:ReadOperationView operations
arca:ReadEventView events
arca:ReadDeltaView state deltas
arca:SubscribeWebSocket real-time stream
arca:ReadAuditLogView audit log entries

Exchange

ActionDescriptionChecked against
arca:PlaceOrderPlace an exchange orderExchange object
arca:CancelOrderCancel an exchange orderExchange object
arca:ReadExchangeView exchange state, positions, and ordersExchange object

Convenience Aliases

Aliases expand to individual actions. Named aliases are expanded at mint time (stored in the JWT). The wildcard arca:* is stored as-is and matches any action at check time.

AliasExpands to
arca:ReadAll arca:Read* + arca:Subscribe + arca:ReadAuditLog
arca:Transferarca:TransferFrom + arca:ReceiveTo
arca:Fundarca:ReceiveTo + arca:WithdrawFrom
arca:Lifecyclearca:CreateObject + arca:UpdateObject + arca:DeleteObject
arca:Exchangearca:PlaceOrder + arca:CancelOrder + arca:ReadExchange
arca:WriteAll write actions
arca:*Everything (evaluated at check time)

Resource Patterns

  • /treasury/usd — exact match
  • /users/* — matches the prefix and all paths below it
  • * — matches all resources

Path safety note: Arca paths have no directory-traversal semantics. The segments . and .. are treated as literal characters. There is no path normalization — what you see is what matches. This is a deliberate security invariant.

Authorization Principles

  1. Deny by default. Anything not explicitly granted by an Allow statement is denied.
  2. Explicit Deny overrides Allow. If any Deny statement matches an (action, resource) pair, the request is blocked — regardless of any Allow statements. Use Deny as a safety rail to protect sensitive paths (e.g., /_internal/*).
  3. Actions are directional and per-resource. For operations touching multiple objects, each side is checked independently. A transfer checks arca:TransferFrom on the source and arca:ReceiveTo on the target.
  4. Inbound permissions are unified. arca:ReceiveTo covers deposits, transfer credits, and sweeps. The balance outcome is identical regardless of channel. Outbound actions (arca:TransferFrom, arca:WithdrawFrom) remain separate because they cross different trust boundaries.
  5. Delete + sweep is compound authorization. Deleting an object with balance requires arca:DeleteObject on the source and arca:ReceiveTo on the sweep target. Without a sweep target, arca:DeleteObject alone suffices for zero-balance objects.
  6. Realm is a boundary, not a resource. Scoped tokens are locked to one realm. Resource patterns do not encode realm — this simplifies matching.

Mint Scoped Token

POST/api/v1/auth/tokenJWT / API Key

Mint a scoped JWT bound to a single realm with IAM-style policy statements. The resulting token is passed from the builder's backend to the end-user's frontend.

Request Body

realmIdstringrequired
The realm this token is locked to.
substringrequired
Opaque end-user identifier (builder-defined, e.g., user ID).
scopeobjectrequired
Policy scope with statements array. Each statement has effect ("Allow" or "Deny", default "Allow"), actions (action strings or aliases), and resources (path patterns).
expirationMinutesnumber
Token TTL in minutes (1–1440). Default: 60.

Response 201 Created

json
{
"success": true,
"data": {
"token": "eyJhbGciOiJIUzI1NiIs...",
"expiresAt": "2026-02-14T15:00:00Z"
}
}

Example

bash
# Builder's backend mints a token for user "alice"
curl -X POST http://localhost:3052/api/v1/auth/token \
-H "Authorization: Bearer arca_78ae7276_..." \
-H "Content-Type: application/json" \
-d '{
"realmId": "6d25623e-...",
"sub": "alice",
"scope": {
"statements": [
{
"effect": "Allow",
"actions": ["arca:Read"],
"resources": ["*"]
},
{
"effect": "Allow",
"actions": ["arca:TransferFrom", "arca:ReceiveTo"],
"resources": ["/users/alice/*"]
},
{
"effect": "Deny",
"actions": ["arca:*"],
"resources": ["/_internal/*"]
}
]
},
"expirationMinutes": 30
}'

Permissions Catalog

GET/api/v1/permissionsNone

Returns the full action catalog with descriptions, grouped by category, plus all available aliases. No authentication required.

Auth Audit Log

GET/api/v1/auth/auditJWT / API Key

Returns a paginated list of authentication and authorization events for the authenticated builder. Tracks sign-ins, token minting, API key usage, and permission denials.

Query Parameters

eventTypestring
Filter by event type: sign_in, token_minted, api_key_authenticated, permission_denied.
realmIdstring
Filter by realm ID.
sincestring
ISO-8601 timestamp — only return events after this time.
untilstring
ISO-8601 timestamp — only return events before this time.
successboolean
Filter by outcome: true for successful events, false for denials.
limitnumber
Max entries per page (1–200, default 50).
offsetnumber
Pagination offset (default 0).

Response 200 OK

json
{
"success": true,
"data": {
"entries": [
{
"id": "...",
"builderId": "...",
"eventType": "token_minted",
"actorType": "builder",
"actorId": "...",
"realmId": "...",
"subject": "alice",
"scopeSummary": "{...}",
"tokenJti": "...",
"expiresAt": "2026-02-21T15:00:00Z",
"success": true,
"operationCount": 12,
"createdAt": "2026-02-21T14:00:00Z"
}
],
"total": 42
}
}

Scoped tokens can access this endpoint with the arca:ReadAuditLog action (included in the arca:Read alias). The operationCount field on token_minted entries shows how many operations were triggered by that token's subject.

Credential Scope Lookup

GET/api/v1/auth/audit/scopeJWT / API Key

Returns the permissions (scope) of a specific credential — either a scoped JWT (by tokenJti) or an API key (by apiKeyId). Use this to inspect what permissions a credential carried when it was used to perform operations.

Query Parameters

tokenJtistring
The JWT ID of a scoped token. Looks up the token_minted audit entry to retrieve the scope.
apiKeyIdstring
The API key ID. Retrieves scope from the api_keys table.

Response 200 OK

json
{
"success": true,
"data": {
"credentialType": "scoped_token",
"credentialId": "abc-123-jti",
"subject": "alice",
"scope": "{\"statements\":[...]}",
"fullAccess": false,
"createdAt": "2026-02-21T14:00:00Z",
"expiresAt": "2026-02-21T15:00:00Z"
}
}

Requires the arca:ReadAuditLog permission. For API keys without a scope, fullAccess is true and scope is null.

Scope Enforcement

When a scoped token (or scoped API key) is used, the API enforces:

  1. Realm lock: Scoped tokens can only access the realm specified at mint time. Requests to other realms return 403 FORBIDDEN.
  2. Policy evaluation: Each operation produces one or more (action, resource) pairs. For each pair: (a) if any Deny statement matches, access is blocked; (b) if any Allow statement matches, access is granted; (c) otherwise, access is denied (implicit deny). All pairs must pass for the request to succeed.
  3. Resource matching: Requested paths must match at least one pattern in the granting statement. Patterns ending in /* match all descendants.

Integration Flow

text
BuilderBackend ArcaAPI EndUserFrontend
| | |
|--- POST /auth/token --->| |
|<-- scoped JWT ----------| |
| | |
|--- hand JWT to user --->| |
| | |
| |<-- POST /transfer -|
| | (Bearer scoped) |
| |-- verify + enforce -|
| | |
| |--- response ------>|

Team & Org Management

Every Arca account belongs to an organization. An org owns one or more realms (demo, production) and hasmembers with defined roles. API keys are org-scoped and grant access to all realms the key holder can reach.

Roles

Org members have one of four roles, listed from most to least privileged:

Role PermissionsOwnerAdminDeveloperViewerView objects & balancesCreate objects & transfersPlace orders & tradeManage API keysInvite & manage membersConfigure realm settings
CapabilityOwnerAdminDeveloperViewer
View realms, objects, balances, operationsYesYesYesYes
Create/modify objects, transfers, ordersYesYesYesNo
Delete objectsYesYesNoNo
Invite/remove members, manage API keysYesYesNoNo
Transfer org ownershipYesNoNoNo

When a member accesses a realm, their org role is mapped to a set of arca:* permissions automatically. Owners and admins get unrestricted access. Developers can do everything except delete objects. Viewers are read-only.

Invitations

Only admins and owners can invite new members. The flow:

  1. Invite — an admin calls POST /api/v1/org/invitations with the recipient's email and desired role. The invitee's role cannot exceed the inviter's.
  2. Email — the recipient receives an invitation link with a JWT token (valid ~7 days).
  3. Preview — the recipient can view the org name and role without signing in (GET /api/v1/invitations/{token}/preview).
  4. Accept — the recipient signs in and accepts the invitation (POST /api/v1/invitations/{token}/accept). They become a member immediately.

Admins can list pending invitations with GET /api/v1/org/invitations and revoke them with DELETE /api/v1/org/invitations/{id}. Recipients can check their own pending invitations with GET /api/v1/invitations/pending.

Realm-Type Scoping

When inviting or updating a member, you can restrict them to specific realm types (e.g., demo only). A scoped member can only access realms matching their allowed types.

Subset rule: you can only assign realm-type scopes that are equal to or narrower than your own. An admin scoped to demo cannot invite someone with production access.

This is useful for giving contractors or junior developers access to demo environments without exposing production data.

API Key Management

API keys are org-scoped and authenticate server-to-server calls. Manage them through the portal or API:

  • Create: POST /api/v1/api-keys — returns the key value once. Store it securely.
  • List: GET /api/v1/api-keys — shows all keys (values are masked).
  • Revoke: DELETE /api/v1/api-keys/{id} — immediately invalidates the key.

To rotate a key: create a new one, update your application to use it, verify it works, then revoke the old key. Never revoke before your application has switched over.

API keys grant full access to every realm in the org. Use scoped tokens (see above) when you need to restrict access to specific resources or actions.

Signing Keys (Operation Non-Repudiation)

Signing keys add a layer of cryptographic non-repudiation to API operations. While API keys prove authentication (who is making the request), signing keys prove authorization (that the builder explicitly approved this specific operation with these specific parameters).

Each signing key is an Ed25519 keypair. The builder generates the keypair locally and registers only the public key with Arca. The private key never leaves the builder's environment.

Key Lifecycle

StateDescription
pendingKey registered but in 72-hour grace period (first key per org activates immediately)
activeKey can be used to sign operations
revokedKey permanently disabled by the builder
rotatedKey replaced by a newer active key (automatically set during activation)
frozenKey suspended by Arca admin (security incident)

Grace Period

When a second (or subsequent) key is registered, it enters a 72-hour grace period before activation. This window allows the builder (or Arca) to detect and cancel unauthorized key registrations. Practice realms skip the grace period for faster iteration.

During activation, the previously active key is automatically transitioned to rotated state.

Audit Trail

Every key lifecycle event (registration, activation, revocation, freeze) is recorded in a hash-chained event log. Each event includes a SHA-256 hash linking it to the previous event, creating a tamper-evident chain. View key events via GET /api/v1/signing-keys/:id/events.

Key Generation

Generate an Ed25519 keypair using OpenSSL or any Ed25519 library:

bash
# Generate private key
openssl genpkey -algorithm Ed25519 -out signing-key.pem
# Extract public key (base64)
openssl pkey -in signing-key.pem -pubout -outform DER | base64

Or in Node.js:

typescript
import { generateKeyPairSync } from 'crypto';
const { publicKey, privateKey } = generateKeyPairSync('ed25519');
const pubKeyBase64 = publicKey.export({ type: 'spki', format: 'der' }).toString('base64');
// Register pubKeyBase64 via POST /api/v1/signing-keys

When to Use Signing Keys

  • Production realms: Signing keys provide cryptographic evidence that all fund movements and trades were authorized by the builder, protecting both the builder and Arca.
  • Compliance: Signed operations create an auditable chain of builder intent that can be independently verified.
  • Multi-person teams: When multiple team members have API access, signing keys attribute specific actions to specific individuals.

Signing is currently optional. Future releases will require signing for production realms. We recommend adopting signing keys early to avoid a migration later.

Common Patterns

Set up a 5-person startup

The founder creates the org (becomes owner). Invite the CTO and lead engineer as admins (they can manage members and API keys). Invite two developers as developers (full access except object deletion).

Give a contractor demo-only access

Invite the contractor as a developer with realm-type scope restricted to demo. They can build and test against demo realms but cannot see or touch production data.

Rotate API keys safely

  1. Create a new API key in the portal or via the API.
  2. Update your application's environment variables to use the new key.
  3. Deploy and verify requests succeed with the new key.
  4. Revoke the old key.

SDK note: org and team management methods live on the ArcaAdmin class (imported from @arca-network/sdk), not the main Arca class. Key methods: createOrg, listMembers, inviteMember, updateMember, removeMember, transferOwnership.

Ownership Transfer

Only the current owner can transfer ownership. The target must already be an admin. After transfer, the previous owner is demoted to admin.

bash
# CLI
arca org transfer-ownership --target-user-id <userId>
# API
POST /api/v1/org/transfer-ownership
{ "targetUserId": "<userId>" }

Custody Model

Arca manages private keys, transaction signing, and on-chain settlement on behalf of builders. You interact with funds through the SDK and API — you never handle private keys or sign transactions directly.

Settlement Lifecycleinitiatedpoint ofno returnsecurearrivalcompletedfailurereversedAfter PNR, source funds are committed. Failures trigger compensating transactions.

Three-Layer Architecture

LayerFunctionSecurity
Wallet ServiceGenerates private keys and signs transactions via a dedicated Rust gRPC serviceEnvelope encryption with HSM-backed key encryption keys; runs in confidential compute pods (hardware-level memory isolation)
Custody PoolsOn-chain addresses that aggregate funds before deployment to exchanges or withdrawalAll movements recorded in the operation ledger with four-bucket conservation invariants (arriving, settled, departing, total)
Exchange AccountsFunds deployed to exchanges (currently Hyperliquid) for tradingArca controls exchange account keys; real-time position and balance tracking via streaming infrastructure

Who Controls What

LayerKey HolderOperation InitiatorRisk Bearer
Wallet ServiceArcaPlatform service (on builder API request)Arca (operational risk)
Custody PoolsArcaPlatform service (automated pool management)Arca + builders (shared)
Exchange AccountsArcaBuilder (via SDK/API trading operations)Builder + their users (market risk); Arca (exchange counterparty risk)
Builder's Arca ObjectsArca (holds keys)Builder (all operations via API)Builder's risk model

Arca holds all private keys and has technical control over all funds at every layer. Builders have operational control — you decide what transactions to execute — but not cryptographic control (you cannot sign transactions directly).

Builder vs. Arca Responsibilities

Arca ManagesBuilder Manages
Private key generation and storageApplication logic and user experience
Transaction signingUser authentication and identity
On-chain settlement and custody poolsKYC/AML compliance for your users (if applicable)
Ledger integrity and conservation invariantsBusiness logic (fee structures, limits, approvals)
Exchange account managementRisk management and position sizing
Real-time streaming and event deliveryClient-side display and error handling

How This Compares

PlatformModelBuilder Builds
ArcaManaged infrastructure — ledger, custody, trading, streamingApplication logic, UX, user auth
PrivyAuth + embedded walletsEverything after authentication (ledger, trading, settlement, streaming)
TurnkeyKey infrastructure (signing API)Everything above key management (ledger, custody pools, trading, streaming)
FireblocksInstitutional custody + MPCApplication layer; designed for institutional treasury, not builder platforms

For Your Legal Team

This section is informational, not legal advice.

Key facts to share with counsel when evaluating Arca:

  • Arca holds all private keys and has technical custody of all funds on the platform.
  • Arca is not a qualified custodian (no bank charter, trust company, or broker-dealer registration).
  • Funds deployed to exchanges carry exchange counterparty risk.
  • Builders are responsible for evaluating their own regulatory requirements based on jurisdiction, user base, and use case.
  • Arca does not provide KYC/AML services — builders using payment links or direct integrations must handle compliance for their own users where required.

Architecture Patterns

When integrating Arca into your product, the key architectural decision is which operations your end-user's frontend performs directly against Arca (using a scoped token) versus which ones route through your backend (using an API key).

The guiding principle: Arca scopes restrict which resources and which actions, but they cannot enforce business logic. If you need to check anything beyond “does this user have permission on this resource” before allowing a write — KYC status, daily limits, manager approval, balance checks in your own system — that write must go through your backend.

Recommended: Read-Only Frontend Tokens

Mint scoped tokens with arca:Read scope for your frontend. This alias includes all read actions plus arca:Subscribe, so real-time streaming works natively. The frontend gets:

  • Real-time balance, position, and order-status streaming via WebSocket
  • Object, operation, event, and delta reads
  • Exchange data reads (order book, positions, fills)
  • Portfolio aggregation and P&L

All mutations (transfers, deposits, withdrawals, object lifecycle) go through your backend using an API key. This gives you:

  • Business logic enforcement before any mutation — KYC checks, daily limits, approval flows
  • Complete server-side audit trail of what was requested and why
  • Minimal blast radius if a frontend token is compromised — read-only means no fund movement
typescript
// Builder's backend — mint a read-only token for the frontend
const { token } = await admin.mintToken({
realmId: realm.id,
sub: user.id,
scope: {
statements: [
{ effect: 'Allow', actions: ['arca:Read'], resources: ['*'] },
],
},
expirationMinutes: 30,
});
// Return 'token' to the frontend
// End-user's frontend — read and stream, no write risk
const arca = Arca.fromToken(token);
const balances = await arca.watchBalances('/users/alice/');
const ops = await arca.watchOperations();

Advanced: Selective Write Scopes

For specific cases where the backend hop adds unacceptable latency, you can selectively add write actions to the frontend token. Consider the risk gradient:

RiskActionsConsiderations
Lowarca:PlaceOrder + arca:CancelOrderOrders are bounded by existing balances and scoped to specific exchange paths. Acceptable for trading UIs where latency matters.
Mediumarca:TransferFromAllows fund movement between objects. Requires careful path scoping. You lose the ability to enforce per-transfer business logic (approval flows, velocity limits).
Higharca:WithdrawFrom, arca:CreateObject, arca:DeleteObjectAffects fund outflows and object lifecycle. Strongly recommend keeping these backend-only. A compromised token with withdrawal scope can drain funds.

Decision Table

OperationRecommendedWhy
Watch balances, positions, order statusFrontend (read-only token)Streaming works natively, zero write risk
List objects, read operations/eventsFrontend (read-only token)Read-only, benefits from low latency
Place/cancel ordersFrontend (advanced) or BackendLow risk if tightly scoped; latency matters for trading
Initiate transfersBackendBusiness logic gating (limits, approvals)
Initiate depositsBackendRequires builder-side validation
WithdrawalsBackendHigh risk — fund outflow must be builder-controlled
Create/delete objectsBackendLifecycle management needs builder orchestration

Production Readiness Checklist

A step-by-step guide for moving from a demo realm to production. Complete these items before launching your integration to real users.

Demo vs. Production realms: Demo realms use simulated settlement, reset weekly, and are free to use. Production realms connect to live exchanges, process real funds, and never reset.

  1. Create a production realm. Use POST /api/v1/realms with type: "production" or create one from the Portal. Production realms connect to live venue infrastructure — objects, balances, and operations are persistent and irreversible.
  2. Generate separate production API keys. Never reuse demo API keys in production. Create dedicated keys for each environment and store them securely (environment variables, secrets manager). Rotate keys periodically by creating a new key, migrating, then revoking the old one.
  3. Scope frontend tokens with minimal permissions. Mint scoped tokens with only the actions your frontend needs — typically arca:Read and streaming. Keep mutations (transfers, orders, object lifecycle) on your backend using the API key. See Architecture Patterns for the recommended split.
  4. Set short token expiry. Use 15–60 minute token lifetimes. On the client, use Arca.fromTokenProvider() to automatically refresh tokens before expiry — the SDK calls your provider function and reconnects the WebSocket seamlessly.
  5. Configure builder fees. Set builderFeeBps on your realm settings for a default fee on all exchange orders, or pass it per-order in placeOrder(). Use feeTargets to split fee revenue to specific paths (e.g. KOL rev-share, referral programs).
  6. Switch from test funding to payment links. fundAccount() and defundAccount() are dev/test helpers that mint or burn simulated funds. In production, use createPaymentLink() to generate hosted deposit and withdrawal pages for your users. Payment links handle KYC, payment processing, and settlement automatically.
  7. Plan your object path hierarchy. Path structure determines how balances aggregate — changing it later requires migrating objects. Design paths to match your product model (e.g. /users/{userId}/wallet for payments, /traders/{traderId}/exchange for trading). See Path Organization.
  8. Implement error handling. Catch OperationFailedError when awaiting operation handles and check error.operation.failureReason. Implement retries with stable nonces (generate the nonce once, reuse on retries). See Troubleshooting.
  9. Enable real-time monitoring. Use watchOperations() to track operation lifecycle and detect failures early. Use watchBalances() to verify settlement completes. For exchange integrations, watchExchangeState() provides live margin and position data.
  10. Complete the Security Pre-flight Checklist. Review every item in the Security Pre-flight Checklist below. It covers API key hygiene, path scoping, scope validation, and token minting safety.

Security Pre-flight Checklist

When exposing Arca to end-users via scoped tokens, the following items remain in the builder's domain. Arca enforces token scope at the API layer, but the builder is responsible for minting correct tokens in the first place.

Review before going live

Each item below represents a security boundary that Arca cannot yet make completely foolproof. Address all of them before shipping scoped-token access to production.

  1. Never expose API keys on the frontend. API keys are team-scoped with full access to every realm. Always use scoped tokens for end-user-facing code. If an API key leaks, revoke it immediately from the API Keys page.
  2. Scope tokens to the user's Arca paths. When minting a token, restrict paths to the current user's subtree (e.g., /users/{userId}/*). A token with paths: ["/*"] gives access to every object in the realm.
  3. Grant minimal permissions. Only include the permissions the user actually needs. Most end users need read and transfer — not delete or create.
  4. Set short expiry times. Frontend tokens should expire in 15–60 minutes. The builder's backend should handle refresh by minting a new token when the current one is close to expiry.
  5. Validate scope server-side before minting. The builder's backend must verify that the requesting user is entitled to the paths and permissions in the token. Arca trusts the builder to get this right — it has no knowledge of the builder's user model.
  6. Do not let the frontend request its own scope. The end-user's frontend should not send the desired scope to the builder's backend. The backend should derive the scope from the user's authenticated identity and business rules.
  7. Use distinct realms for dev and production. Demo realm data is simulated and has permissive defaults. Never use a demo realm for real transactions.
  8. Treat operation paths as idempotency keys. If a transfer path is reused, the original result is returned (no double-spend). Build unique paths — include a nonce or UUID (e.g., /op/transfer/{userId}-{uuid}).
  9. Monitor via events. Subscribe to realm events (WebSocket or polling) to detect unexpected activity — transfers you did not initiate, objects created outside your expected paths, etc.

Error Handling

Every API error returns the same JSON envelope with a machine-readable code you can match on programmatically and a human-readable message with details.

Response Envelope

Successful responses:

json
{
"success": true,
"data": { ... }
}

Error responses:

json
{
"success": false,
"error": {
"code": "VALIDATION_ERROR",
"message": "Realm name must be 100 characters or fewer",
"errorId": "err_a1b2c3d4"
}
}

The errorId field is a unique identifier for the error occurrence. It is always present on 5xx errors and on most 4xx errors. When contacting support about an error, include the errorId so we can locate the exact request in our logs.

Validation Errors (400)

CodeDescription
VALIDATION_ERRORGeneral input validation failed (missing fields, format errors, constraint violations)
MISSING_PARAMA required parameter was not provided
INVALID_AMOUNTAmount is not a valid number, is negative, or exceeds limits
INVALID_PATHObject path violates naming rules (reserved prefix, invalid characters, parent conflict)
INVALID_TYPEObject type is not one of the allowed types
INVALID_REQUESTRequest body is malformed or contains contradictory parameters

Business Logic Errors (400)

CodeDescription
INSUFFICIENT_BALANCEThe source object does not have enough settled balance for this transfer or order
OBJECT_NOT_READYThe object exists but is not yet in a state where this operation is allowed
DELETION_BLOCKEDObject cannot be deleted (non-zero balance, in-flight operations, or active exchange positions)
ORDER_FAILEDThe exchange rejected the order (insufficient margin, invalid size, or market-specific constraints)
MARKET_DATA_UNAVAILABLEPrice data for the requested market is not currently available
DIRECT_EXCHANGE_TRANSFER_NOT_ALLOWEDTransfers directly between two exchange objects are not supported; route through a denominated object
SIGNIN_FAILEDInvalid email or password

Authentication Errors (401)

CodeDescription
UNAUTHENTICATEDNo valid credential provided, or the token/API key has expired or been revoked

Authorization Errors (403)

CodeDescription
FORBIDDENThe token scope or role does not permit this action on this resource
ADMIN_REQUIREDThis endpoint requires admin-level access
INSUFFICIENT_ROLEYour org role is below the minimum required for this action (e.g., Developer trying an Admin-only operation)
REALM_SCOPE_MISMATCHThe scoped token or API key does not have access to the requested realm

Not Found Errors (404)

CodeDescription
NOT_FOUNDGeneric resource not found
REALM_NOT_FOUNDNo realm with the specified ID or slug
OBJECT_NOT_FOUNDNo Arca object with the specified ID or path in this realm
OPERATION_NOT_FOUNDNo operation with the specified ID
ORDER_NOT_FOUNDNo order with the specified ID on this exchange
USER_NOT_FOUNDNo user account with the specified identifier
ORG_NOT_FOUNDNo organization with the specified ID
MEMBER_NOT_FOUNDNo member with the specified ID in this organization
INVITATION_NOT_FOUNDNo pending invitation with the specified ID
ACCOUNT_NOT_FOUNDNo exchange account linked to this object
PROFILE_NOT_FOUNDNo user profile found for the authenticated user
PAYMENT_LINK_NOT_FOUNDNo payment link with the specified ID

Conflict Errors (409)

CodeDescription
CONFLICTGeneric conflict (duplicate resource or invalid state transition)
ALREADY_EXISTSA resource with this identifier already exists (realm slug, object path, API key name)
ALREADY_MEMBERThe invited user is already a member of this organization
ALREADY_DELETEDThe resource has already been deleted
IDEMPOTENCY_VIOLATIONThe operation path was reused with different parameters (same path must have same inputs)
INVITATION_EXPIREDThe invitation has passed its expiration window
INVITATION_PROCESSEDThe invitation has already been accepted or rejected
INVITATION_REVOKEDThe invitation was revoked by an admin before it could be accepted
PAYMENT_LINK_EXPIREDThe payment link has passed its expiration time
PAYMENT_LINK_COMPLETEDThe payment link has already been used

Server Errors (5xx)

CodeHTTP StatusDescription
INTERNAL_ERROR500Unexpected server error
EXCHANGE_ERROR502The upstream exchange returned an error
EXCHANGE_PROVISION_FAILED502Failed to provision an exchange account for this object
TEMPORAL_ERROR502Failed to start or signal the background workflow for this operation
EXCHANGE_UNAVAILABLE503The exchange is temporarily unreachable
SERVICE_UNAVAILABLE503An internal service is temporarily unreachable
Note on 5xx messages: For security, the message field on 500 and 502 errors is replaced with a generic string. Match on the code field for programmatic handling, and include the errorId when contacting support.

Error Handling in the SDK

The TypeScript SDK throws typed errors you can catch and inspect:

typescript
import Arca, { ArcaError, OperationFailedError } from '@arca/sdk';
const arca = new Arca({ apiKey: 'arca_...' });
try {
const { nonce } = await arca.nonce('/transfers/payment-001');
await arca.transfer({
from: walletId,
to: recipientId,
amount: '100.00',
nonce,
});
} catch (err) {
if (err instanceof OperationFailedError) {
// Operation was created but failed during execution
console.error('Operation failed:', err.operation.failureMessage);
} else if (err instanceof ArcaError) {
// API-level error — match on err.code
switch (err.code) {
case 'INSUFFICIENT_BALANCE':
console.error('Not enough funds:', err.message);
break;
case 'IDEMPOTENCY_VIOLATION':
console.error('Path reused with different params');
break;
default:
console.error(`[${err.code}] ${err.message}`);
}
}
}

Troubleshooting

Common problems organized by scenario. For the full error code catalog, see the Error Handling section above.

First-Hour Mistakes

The five most common issues during initial integration:

SymptomCauseFix
REALM_NOT_FOUND on every callMissing await arca.ready() before using SDK methodsAlways call await arca.ready() after constructing the Arca client
IDEMPOTENCY_VIOLATIONCalling nonce() inside a retry loop (generates a new key each time)Generate the nonce once, then reuse the same nonce on retries
Object already exists but has different typeCalling ensureDenominatedArca() at a path that has a different object typeUse a different path, or delete the existing object first with ensureDeleted()
INVALID_COIN on market data callsUsing bare symbols ("BTC") instead of canonical IDs ("hl:BTC")Always use the full canonical format: "hl:BTC", "hl:1:TSLA"
WebSocket events not arrivingNot calling watchOperations() or watchBalances() before expecting eventsSubscribe to the relevant watch stream before performing operations

Scoped Token Debugging

When a scoped token request is rejected with FORBIDDEN, the error message says which action was denied but does not reveal the full policy. Here's how to debug:

  1. Check the error response. The message field includes the denied action (e.g. "action arca:Transfer not allowed").
  2. Decode the token. Scoped tokens are JWTs. Paste yours into a JWT debugger to see the scope claim — it lists every (action, resource) pair the token grants.
  3. Compare action names. The denied action must exactly match one of the scope entries. Common mismatches: using arca:Write when the endpoint requires arca:Transfer, or scoping to /users/alice/* when the request targets /users/bob/*.
  4. Check path scoping. If the scope grants arca:Read on /users/alice/*, the token can read Alice's objects but not Bob's. Broaden the path to /users/* if the client needs access to multiple users.

Settlement Failure Diagnosis

When an operation reaches a failed terminal state, thefailureReason field explains why:

Failure ReasonWhat happenedRecovery
INSUFFICIENT_BALANCESource account didn't have enough settled funds at execution timeVerify balances before the transfer; for exchange operations, check margin
EXCHANGE_REJECTEDThe exchange venue rejected the order (margin, leverage, or market condition)Check failureMessage for the venue's specific rejection reason
EXPIREDThe settlement window elapsed before the operation could completeRetry with a new nonce if the underlying condition is resolved
OBJECT_NOT_FOUNDThe source or destination object was deleted during settlementVerify both objects exist before retrying

Failed operations are permanent — they cannot be retried directly. Create a new operation with a new nonce to retry the intent. For transfers that fail after PNR (point of no return), Arca automatically creates a compensating transaction to reverse the source debit.

Exchange Order Failures

ScenarioCauseRecovery
Order rejected: insufficient marginExchange equity minus existing positions doesn't cover the new order's margin requirementReduce order size, increase leverage (if appropriate), or fund the exchange object
Order rejected: leverage mismatchRequested leverage differs from the active leverage for that coinCall updateLeverage() first, then place the order
Max-size order rejectedMax order size dropped more than the maxSizeTolerance (default 2%) between display and executionRefresh the displayed max and let the user confirm again; increase tolerance if appropriate
Order fills at unexpected priceMarket orders execute at the current market price, which may differ from the displayed mid priceUse limit orders for price-sensitive trades; display the order book spread to users

When to Contact Arca Support

Most issues are resolvable using the error codes and troubleshooting steps above. Contact Arca support (support@arcaos.io) when:

  • You receive a 5xx error that persists across retries — include the errorId
  • An operation is stuck in processing state for more than 5 minutes
  • Balance totals don't match what you expect after all operations have settled
  • The WebSocket connection fails to reconnect after multiple attempts
  • You need help designing your path hierarchy or permission model