Skip to content

Trading Call Flow

This is the order to call the API in. Start from a managed account that has never traded and finish with a strategy placing real orders on Hyperliquid.

Open the diagram full screen ↗

Fund the account before you approve the agent

Hyperliquid will not accept an agent approval from an account with no equity. The API reports that rejection as a plain 500, so it looks like a server fault when it is really an empty account. Deposit first, confirm equity, then approve.

Orders are placed indirectly

There is no standalone place-order endpoint. Orders come from an armed advanced strategy or from a playlist action. If you want one ad-hoc order, put it in a playlist.

Binding details live on the Hyperliquid page

Which key signs what, agent naming rules, expiry limits and the full approval surface are on Hyperliquid Connection. This page only covers the order.


1. Pick the account

Open ↗

GET /api/managed-accounts

Save the id, the address (the Hyperliquid master address) and the customerId. Every {id} below is that account id, and you need all three values in the steps that follow.

2. Check for an existing binding

Open ↗

GET /api/managed-accounts/hyperliquid-binding/accounts?customerId={customerId}

data: null means this customer has no binding yet and you can start at step 3. If an account comes back, a binding already exists and may still be pending. Use that account for the rest of the flow instead of binding a second one.

3. Resolve the deposit inputs

Open ↗

GET /api/configs
GET /api/assets?chainId={chain}&address={usdcAddress}
GET /api/venues?status=active&search=Hyperliquid

Start with /api/configs. It is an open read-only route, so no token is needed. Read the chain list from it, pick a chain that supports deposits, and take that chain's USDC address and its forwardFee. It has no entry in the API reference, which is why step 3 has no link of its own.

Then /api/assets gives you the USDC id and decimals. The venue list is under Venues ↗: take the item with slug: "hyperliquid" and save its id as venueId.

4. Create the deposit playlist

Open ↗

POST /api/playlists
{
  "accountId": "{id}",
  "customerId": "{customerId}",
  "name": "Deposit USDC to Hyperliquid perp",
  "status": "active",
  "strategyTemplateId": "hyperliquid_deposit",
  "stopConditions": [
    {
      "scope": { "scope": "playlist" },
      "type": "execution_count",
      "config": { "maxExecutions": 1 },
      "actionOnTrigger": "complete",
      "enabled": true
    }
  ],
  "steps": [
    {
      "order": 1,
      "stepType": "trigger",
      "name": "Instant deposit request",
      "type": "instant",
      "config": { "type": "instant" },
      "enabled": true
    },
    {
      "order": 2,
      "stepType": "action",
      "action": "deposit",
      "venue": "{venueId}",
      "product": "hyperliquid-perp",
      "assetId": "{assetId}",
      "asset": "USDC",
      "assetAddress": "{usdcAddress}",
      "assetDecimals": 6,
      "amount": "30000000",
      "amountSource": { "type": "static", "unit": "token" },
      "chain": "{chain}",
      "params": { "source": "hyperliquid_deposit_sheet", "hyperCoreDestination": "perp" }
    }
  ]
}

amount is in base units, not USDC. At 6 decimals, 30 USDC is "30000000". The amount also has to clear the chain's forwardFee, which is deducted on arrival, so a deposit at or below the fee lands as nothing.

Swap "perp" and "hyperliquid-perp" for "spot" and "hyperliquid-spot" if you are funding the spot side.

5. Wait for the funds

Open ↗

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

Poll until equity is above zero. The money has to cross from the chain to Hyperliquid, so this is not instant. Do not move to step 6 on a zero balance.

6. Approve the agent

Open ↗

POST /api/managed-accounts/{id}/hyperliquid-binding/approve
{
  "venueAccountAddress": "{account address}",
  "agentName": "paros-agent",
  "expiryDays": 30,
  "isRevoke": false
}

Send expiryDays (1 to 180) or expiresAt, never both. agentName takes up to 16 characters: letters, digits, spaces, - and _.

Give this call at least 180 seconds

Approve blocks while the Salt signing ceremony runs and can take well over a minute. A client timeout of 60 seconds will fire on a call that is still working.

7. Confirm the binding

Open ↗

GET /api/managed-accounts/{id}/hyperliquid-binding/approval-status

Setup is complete when the response shows approved: true and agents[] holds the agent's agentAddress. If approved is false and agents is empty, the binding is still pending: re-check equity in step 5, then call approve again.

A 409 means do not retry approve

409 already in progress means your first approve call is still running. Retrying starts a second signing ceremony. Poll this endpoint until it resolves.

8. Builder fee (optional)

Open ↗

POST /api/managed-accounts/{id}/hyperliquid-binding/builder-fee
{ "isRevoke": false }

Read it back with GET on the same path, which returns approved and maxFeeRate. This one is master-signed, so it waits on custody like the approval did.

9. Read the market

Open ↗

GET /api/managed-accounts/{id}/hyperliquid/markets
GET /api/managed-accounts/{id}/hyperliquid/markets/{symbol}/state

Use symbol (for example "ETH") as the productId when you build the strategy. Market state returns markPrice, funding and the numbers you need to set rule thresholds.

10. Create the strategy draft

Open ↗

POST /api/advanced-strategies
{
  "accountId": "{id}",
  "customerId": "{customerId}",
  "name": "ETH funding carry",
  "metadata": { "templateId": "funding_carry" },
  "strategyClass": "generative",
  "level": 2,
  "setupConfig": {},
  "setupProductId": "ETH",
  "riskConfig": {
    "maxLeverage": "4",
    "maxDrawdownPct": "12",
    "allowedVenueIds": [],
    "deniedProductIds": []
  },
  "rules": [
    {
      "order": 1,
      "name": "Enter carry",
      "enabled": true,
      "cooldownSeconds": 3600,
      "conditions": [
        {
          "role": "trigger",
          "key": "funding_rate",
          "operator": "greater_than",
          "order": 0,
          "config": { "ratePctPerHour": "0.02", "productId": "ETH" }
        }
      ],
      "actions": [
        {
          "key": "place_order",
          "order": 1,
          "custodyGate": "agent",
          "config": {
            "productId": "ETH",
            "side": "buy",
            "notionalUsd": "20",
            "orderType": "market",
            "reduceOnly": false
          }
        }
      ]
    }
  ]
}

A rule that contains place_order needs cooldownSeconds above zero. Actions inside one rule are either all generative or all protective, and mixing the two is rejected. Leverage is capped by riskConfig.maxLeverage.

To change a draft afterwards, PUT /api/advanced-strategies/{id}/draft with { "name"?, "metadata"?, "definition": { ...same fields... } }.

11. Validate

Open ↗

POST /api/advanced-strategies/{id}/validate

No body. If it returns issues, fix them through step 10 and validate again before you publish.

12. Publish

Open ↗

POST /api/advanced-strategies/{id}/publish

No body. A published strategy still does nothing; it has no permission to trade until you arm it.

13. Arm

Open ↗

POST /api/advanced-strategies/{id}/arm
{}

Pass { "versionId": "<uuid>" } to arm a specific version. Arming needs the binding from step 7 to be approved: true. Once armed, the server evaluates your rules and places orders through the agent.

To test the same rules without sending real orders, call POST /api/advanced-strategies/{id}/dry-run instead of arming. It runs the same rules and sends no orders.


When a step fails

Symptom Where it breaks What to do
500 from approve Step 6, account still unfunded Check equity in step 5 and fund again
409 from approve Step 6, a ceremony is already running Poll step 7, do not call approve twice
Timeout on approve Step 6, client gave up early Raise the timeout to 180 seconds and poll step 7
approved: false, agents: [] Step 7, binding not finished Re-check equity, then approve again
Arm refused Step 13, binding not approved Finish step 7 first
Validation issues Step 11, bad rule shape Fix the definition, PUT the draft, validate again