Skip to content

Managed Accounts

A managed account is the custody account Paros operates for you. It holds the assets, it carries the address that venues see, and every strategy and playlist you create points at its id.

Open in the reference ↗


What is on an account

Field Meaning
id The identifier every strategy, playlist and position call takes
name Your label for the account
address The on-chain address. For Hyperliquid this becomes the master address
customerId The owning customer
agentId The trading agent bound to the account
status Lifecycle state: active, inactive or paused
orchestrationContractAddress The orchestration contract, where the account has one

Create an account

Managed Accounts ↗

POST /api/managed-accounts
{ "saltAccountId": "<salt account uuid>" }

saltAccountId is required. The managed account is built from an existing Salt custody account, so the optional X-Salt-Token header applies to this call.

Open the diagram full screen ↗

Worked example: a 3-of-5 account

The vault comes first. Five signers, three required to move funds, created on the Salt side. Paros never creates it and never counts signatures; it binds the finished vault to a managed account.

{
  "signers": ["0xA1...", "0xB2...", "0xC3...", "0xD4...", "0xE5..."],
  "threshold": 3
}

Custody returns the vault id once the signer set is final. That id is the only thing the Paros call needs:

POST /api/managed-accounts
X-Salt-Token: <session token>
{ "saltAccountId": "3f7c1e9a-..." }

The answer arrives before the account is usable:

{ "jobId": "b41d...", "saltAccountId": "3f7c1e9a-...", "saltOrgId": "...", "status": "pending" }

Poll until the status settles:

GET /api/managed-accounts/{id}
{ "id": "...", "status": "active", "address": "0x...", "saltSigners": ["0xA1...", "0xB2...", "0xC3...", "0xD4...", "0xE5..."] }

saltSigners comes back with all five. The threshold is not stored by Paros, so read it from the vault when you need it. A 409 here means that vault is already bound, and a vault whose organisation differs from the customer's is refused outright.

This returns 202, not 201

Creation is asynchronous. The response acknowledges the request and the account becomes usable once the custody side finishes. Poll GET /api/managed-accounts/{id} until status settles before you point a strategy at the account.

A 409 means that custody account is already bound to a managed account. Read the existing one instead of sending the create again.

A new account starts with no policies of its own. It inherits whatever the customer has already set, which is covered under Policies on an account.


List and read

Managed Accounts ↗

GET /api/managed-accounts
GET /api/managed-accounts/{id}

The list takes optional filters: page, limit, status, customerId, saltOrgId, agentId, search, sortBy, sortOrder.

Filter by customerId when you know the owner, and by agentId when you are tracing which account a trading agent sits on.

A 404 on the single read means the id does not exist or is not visible to the caller.


Update an account

Managed Accounts ↗

PUT /api/managed-accounts/{id}
{
  "name": "Northwind main",
  "agentId": "<uuid>",
  "status": "active",
  "orchestrationContractAddress": "0x...",
  "stepInTriggerCategory": "manual",
  "stepInReason": "operator pause"
}

Every field is optional, so send only what changes.

agentId and orchestrationContractAddress are signer-relevant, which means the optional X-Salt-Token header applies to those updates. A 409 signals a conflict, most often an address that another account already uses.


Policies on an account

Policies decide what an account is allowed to do. Each one is a row with a type and a config, and it either blocks execution or records a preference. The account-level ones sit here; the customer-level ones are described under Customers, and both apply at once.

GET  /api/managed-accounts/{accountId}/policies
GET  /api/managed-accounts/{accountId}/policies/enforced
GET  /api/managed-accounts/{accountId}/policies/effective-config
POST /api/policies
PUT  /api/policies/{id}
DELETE /api/policies/{id}

GET .../policies returns the account's own rows. .../enforced returns what is actually in force, customer policies included. .../effective-config returns the merged config the execution path reads. When a strategy is refused and you cannot see why, read the enforced list first.

These endpoints are not in the public reference yet

The policy routes are tagged internal in the API spec, so they do not appear under /api-docs. Until they do, the calls above are the contract.

The types that block execution

Type Config What it does
whitelist sendWhitelistOnly, whitelists[] Restricts transfers to listed addresses
blacklist addresses[] Refuses listed addresses
limit enabled, maxTokenAmt, maxUsdAmt Caps each transaction by token and by USD
venue allowedVenues[] Hard allowlist of venue slugs
exposure — Reserved. The shape is a placeholder, so no exposure limit is enforced today
hyperliquid spotReserveFloorUsd Spot USDC kept back from transfers, withdrawals and strategies

routing (dexOrder[]) and gas (alertBelowThreshold, gasManagement) are the two preference types. They steer behaviour, and they never block a transaction.

What creates a default

Nothing. Creating a managed account writes no policy rows, so a new account is unrestricted at this layer until you add something. It still inherits the customer's policies, and that is usually where an account gets its first constraint.

sendWhitelistOnly defaults to true when you create a whitelist policy, so a whitelist takes effect at the moment it exists unless you explicitly turn the switch off.

Open the diagram full screen ↗

How the two scopes combine

Customer policies cover every account. Account policies apply on top, and the rules differ by type:

  • Whitelist. The account's addresses must be a subset of the customer's. An account can narrow the customer list; it cannot widen it.
  • Blacklist. Entries are unioned. An address blocked at either level is blocked.
  • Everything else. The nearest row wins, and an account row replaces the customer's for that type.

Who may write

Only a customer user (owner or member) can create a policy. Paros staff cannot write one on your behalf. A policy attached to an account also requires the caller to be a signer on that account, and a customer-wide policy requires owner authority.


Retire an account

Retiring is a guarded transition. The account moves to inactive and stays on record.

Check what would block deactivation

Managed Accounts ↗

GET /api/managed-accounts/{id}/deactivation-check

The pre-flight read reports blockers, warnings and toArchive, the last being how many draft and paused strategies would be archived. Deactivation is rejected while blockers remain: active strategies, running executions, open Hyperliquid orders or TWAPs.

Deactivate

POST /api/managed-accounts/{id}/deactivate

No body. Deactivation archives your draft and paused strategies, revokes the account's Hyperliquid agent and sets the account to inactive. An inactive account cannot run or create strategies, bind venues, or receive transfers. A 409 means a blocker is still there.

An inactive account is still on record, still readable, and its history is intact. Count it as the end state: there is no step after this one.


Custody boundary

Creating an account and updating its signer-relevant fields both pass through the custody layer. The failures below come from that boundary, not from the account resource:

Status What happened
403 Signing policy refused the call. If your organisation requires co-sign, a call without a valid X-Salt-Token lands here
419 The Salt token expired. Re-authenticate and retry
502 / 504 The custody service or a downstream RPC did not respond

After a 502 or 504, read the account before resending a create or an update.