> ## Documentation Index
> Fetch the complete documentation index at: https://docs.allocait.app/llms.txt
> Use this file to discover all available pages before exploring further.

# API quickstart

> Create a key, read a portfolio, run a check and handle a refusal.

After you create a key, four calls take you to a first check: list your portfolios, see what one holds, check it at prices you supply, and read a refusal.

Illustrative example. The figures are hypothetical, not a recommendation.

Allocait states facts about your own rules. A result inside every range does not say a change is advisable. Allocait cannot approve, stop or place an order.

## Before you start

You need an account with agent access, the holdings you want checked and at least one allocation band. If you haven't set those up, follow the [app quickstart](https://docs.allocait.app/quickstart.md) first. A **Read only** key is enough here, because every call on this page only reads.

**1. Create a key**

In the app, open **Settings > API keys** and select **Create API key**. Enter a name, choose **Read only**, and select **Create key**. Copy the key before you select **Done**. Allocait shows a key once and stores only its hash.

Keep the key in an environment variable. Every request sends it in the `x-api-key` header.

```bash theme={null}
export ALLOCAIT_API_KEY="YOUR_API_KEY"
```

[Authentication](https://docs.allocait.app/api/authentication.md) covers permissions and access levels.

**2. List your portfolios**

```bash theme={null}
curl "https://allocait.app/api/v1/portfolios" \
  -H "x-api-key: $ALLOCAIT_API_KEY"
```

A new account starts with a `Main Portfolio`. Every list route returns `data` and `has_more`.

```json theme={null}
{
  "data": [
    {
      "portfolio_id": "919bbfb9-c2e7-4c4c-9d6d-d20f83330245",
      "name": "Main Portfolio",
      "description": "Your primary investment portfolio",
      "base_currency": "USD",
      "active_scenario_id": "3f2e940e-c6d0-4710-ac50-7d981b4ef2dc",
      "active_scenario_name": "My rules",
      "position_count": 3
    }
  ],
  "has_more": false
}
```

Copy `portfolio_id`. The next calls need it. Allocait supports US dollars only, so `base_currency` is always `USD`.

**3. See what needs a price**

```bash theme={null}
curl "https://allocait.app/api/v1/portfolio/snapshot?portfolio_id=YOUR_PORTFOLIO_ID" \
  -H "x-api-key: $ALLOCAIT_API_KEY"
```

The snapshot returns what you hold in shares and your rules. It returns no price of its own, so the total, the percentages, the exposure, the status and each non-cash value come back `null`. `symbols_to_price` lists the symbols you need to price. Cash needs no price. Neither does an option with a mark, the per-share premium you recorded for it, which Allocait counts as its price. This response is trimmed: `rules`, `monitors`, `generated_at` and some position fields are left out.

```json theme={null}
{
  "scenario_id": "3f2e940e-c6d0-4710-ac50-7d981b4ef2dc",
  "total_value": null,
  "price_source": "unpriced",
  "exposure": null,
  "symbols_to_price": ["ALFA", "BRAV", "GOLF"],
  "positions": [
    { "symbol": "ALFA", "shares": 250, "asset_class": "Equities", "current_price": null, "position_value": null, "position_pct": null },
    { "symbol": "BRAV", "shares": 200, "asset_class": "Fixed Income", "current_price": null, "position_value": null, "position_pct": null },
    { "symbol": "GOLF", "shares": 300, "asset_class": "Commodities", "current_price": null, "position_value": null, "position_pct": null },
    { "symbol": "$CASH", "shares": 17095, "asset_class": "Cash", "current_price": 1, "position_value": 17095, "position_pct": null }
  ],
  "compliance": null
}
```

See [Prices](https://docs.allocait.app/concepts/prices.md) for why Allocait never fills a price in.

**4. Run your first check**

Get a price for each symbol in `symbols_to_price` from your own source, then send them with the same portfolio.

```bash theme={null}
curl -X POST "https://allocait.app/api/v1/portfolio/snapshot" \
  -H "x-api-key: $ALLOCAIT_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "portfolio_id": "YOUR_PORTFOLIO_ID",
    "prices": { "ALFA": 181.5, "BRAV": 94.2, "GOLF": 62.3 }
  }'
```

The values and the check now come from your prices. This response is trimmed the same way. `exposure` shows only its totals here; it also lists `classes` and `holdings`.

```json theme={null}
{
  "total_value": 100000,
  "price_source": "overrides",
  "exposure": { "total_value": 100000, "long_pct": 82.91, "short_pct": 0, "net_pct": 82.91, "gross_pct": 82.91, "options_counted": 0, "options_not_counted": 0 },
  "symbols_to_price": [],
  "positions": [
    { "symbol": "ALFA", "shares": 250, "asset_class": "Equities", "current_price": 181.5, "position_value": 45375, "position_pct": 45.375 },
    { "symbol": "BRAV", "shares": 200, "asset_class": "Fixed Income", "current_price": 94.2, "position_value": 18840, "position_pct": 18.84 },
    { "symbol": "GOLF", "shares": 300, "asset_class": "Commodities", "current_price": 62.3, "position_value": 18690, "position_pct": 18.69 },
    { "symbol": "$CASH", "shares": 17095, "asset_class": "Cash", "current_price": 1, "position_value": 17095, "position_pct": 17.095 }
  ],
  "compliance": {
    "status": "violations",
    "issue_count": 1,
    "class_violations": [
      { "asset_class": "Commodities", "status": "ABOVE_MAX", "current_pct": 18.69, "min_pct": 4, "max_pct": 13, "outside_by": { "edge": "above_max", "value": 5690, "pct_points": 5.69 } }
    ],
    "size_violations": [],
    "unchecked": []
  }
}
```

Read `compliance.status`:

| `status` | Meaning |
| - | - |
| `compliant` | No class or size range has a breach, and `unchecked` is empty. |
| `violations` | At least one class or size range has a breach. |
| `partial` | Nothing judged has a breach, but `unchecked` lists holdings no rule judged. |
| `unjudged` | Nothing could be judged, for example a scenario with no rules or a portfolio worth zero or less. It lists no breach, and `issue_count` is 0. |

Here Commodities is 18.69% of the portfolio against a range of 4 to 13%. `outside_by` states how far: 5.69 percentage points above the maximum, or 5,690 dollars at your prices. It describes where the rule stands. It doesn't say what to do.

`POST /compliance/check` returns this `compliance` block on its own. It needs a price for every non-cash position, except a held option with a mark. `scenario_id` is optional: left out, the portfolio's active scenario is used, and a portfolio with none set is refused. To ask about a change before you place it elsewhere, use [Check a change](https://docs.allocait.app/guides/check-before-you-trade.md).

**5. Handle a refusal**

Send `/compliance/check` a price map that leaves out two held symbols. The call uses the portfolio's active scenario.

```bash theme={null}
curl -X POST "https://allocait.app/api/v1/compliance/check" \
  -H "x-api-key: $ALLOCAIT_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "portfolio_id": "YOUR_PORTFOLIO_ID",
    "prices": { "ALFA": 181.5 }
  }'
```

The call fails with `400`. Every refusal a route produces uses this envelope.

```json theme={null}
{
  "error": "prices must cover every non-cash position in the portfolio",
  "error_code": "PRICES_INCOMPLETE",
  "missing_symbols": ["BRAV", "GOLF"]
}
```

Build your code around these rules:

* Treat any non-2xx response as no result. It isn't a pass and it isn't a finding.
* Branch on `error_code`, and show `error` to a person. Four answers carry no `error_code`, so branch on their status: `401`, the `403` for an account with no agent access, the `403` for a key without read permission, and the `429` for the request budget.
* Change the request before you retry a `400`, `403`, `404` or `409`. The same request usually fails the same way.
* Wait for `Retry-After`, then retry a request-budget `429`. A `429` with `error_code: DAILY_LIMIT_EXCEEDED` has no `Retry-After`; don't retry it on a timer.
* Retry a `500` once only for a read, or for a write that is safe to repeat (see [Idempotency](https://docs.allocait.app/api/idempotency.md)). Quote its `request_id` when you contact support.

Every response that reaches a route carries `X-RateLimit-Limit` (`60`) and `X-RateLimit-Remaining`, the requests left in the rolling 60-second window. See [API errors](https://docs.allocait.app/api/errors.md) and [Rate limits](https://docs.allocait.app/api/rate-limits.md).

## Next steps

* [Check a change](https://docs.allocait.app/guides/check-before-you-trade.md): which of your rules a change puts outside range.
* [Update with an agent](https://docs.allocait.app/guides/update-with-an-agent.md): set a holding to the share count you hold now. Writes need a key with **Read and change** access.
* [Idempotency](https://docs.allocait.app/api/idempotency.md): make a retried cash movement safe.
* [Endpoints by task](https://docs.allocait.app/api/endpoints-by-task.md): every task with its route and its MCP tool. Open **Endpoints** in the API reference sidebar for every operation with request and response examples.
* [Connect an agent](https://docs.allocait.app/connect-an-agent.md): the same reads and checks as tools for an agent.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.