# Org API: everything the dashboard does

Org keys drive every dashboard action through the API: agents, budgets, approvals, receipts, billing, webhooks and team.

Anything a person can do in the dashboard, an agent can do through the API. Agent keys (`sk_…`) buy results; **org keys** (`ok_…`) run the org: make agents and set their budgets, decide approvals, read receipts, top up, manage webhooks and the team.

## Org keys

- An owner makes them on the Team page while signed in (or `POST /v1/orgs/{orgId}/keys` with a session). The key is shown once.
- Scope `read` can only read; anything else gets `403 forbidden` ("This org key is read-only"). Scope `write` can do anything its maker can do on the dashboard.
- A key acts as the owner who made it, works only on `/v1/orgs/{orgId}/…` for its own org, and stops working if that owner leaves the org.
- Keys can't make or revoke keys, and can't ask for data deletion: both need a person signed in.
- Up to 20 active keys per org. 300 requests a minute per key; see [rate limits](/docs/rate-limits).

Send it as `Authorization: Bearer ok_…`. The org's id is in the dashboard's URLs (`?org=…`) and in `GET /v1/me`.

## Worked examples

### Make an agent and read its key

**curl**

```bash
curl https://api.arettic.com/v1/orgs/$ORG_ID/agents \
  -H "Authorization: Bearer $ARETTIC_ORG_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "name": "Lead research", "mode": "live", "monthly_budget_credits": 100000 }'
# → { "agent": { "id": "…", … }, "key": "sk_live_…", "key_note": "…" }  (the key is shown once)
```

**TypeScript**

```ts
import { AretticOrg } from "@arettic/sdk";

const org = new AretticOrg({ apiKey: process.env.ARETTIC_ORG_KEY, orgId: "your-org-id" });
const { agent, key } = await org.agents.create({ name: "Lead research", mode: "live" });
await org.agents.update(agent.id, { monthly_budget_credits: 100_000, approval_threshold_credits: 20_000 });
```

**Python**

```python
import os

from arettic import AretticOrg

org = AretticOrg(os.environ["ARETTIC_ORG_KEY"], os.environ["ARETTIC_ORG_ID"])
made = org.agents.create(name="Lead research", mode="live")
org.agents.update(made["agent"]["id"], monthly_budget_credits=100_000, approval_threshold_credits=20_000)
```

### A month of receipts, and the CSV

**TypeScript**

```ts
const page = await org.receipts.list({ from: "2026-09-01", to: "2026-09-30", limit: 200 });
const csv = await org.receipts.exportCsv({ from: "2026-09-01", to: "2026-09-30" });
```

**Python**

```python
page = org.receipts.list(from_="2026-09-01", to="2026-09-30", limit=200)
csv = org.receipts.export_csv(from_="2026-09-01", to="2026-09-30")
```

### Decide an approval

**curl**

```bash
curl https://api.arettic.com/v1/orgs/$ORG_ID/approvals/$APPROVAL_ID/decision \
  -H "Authorization: Bearer $ARETTIC_ORG_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "decision": "approve", "note": "Q4 list" }'
```

**TypeScript**

```ts
const pending = await org.approvals.list({ status: "pending" });
for (const a of pending) await org.approvals.decide(a.approval_id, { decision: "approve" });
```

**Python**

```python
for a in org.approvals.list(status="pending")["approvals"]:
    org.approvals.decide(a["approval_id"], "approve")
```

### Add a webhook

**TypeScript**

```ts
const hook = await org.webhooks.create({ url: "https://example.com/arettic-hook", events: ["job.completed"] });
// hook.webhook.secret is shown once: store it to verify signatures
```

**Python**

```python
hook = org.webhooks.create("https://example.com/arettic-hook", events=["job.completed"])
secret = hook["webhook"]["secret"]  # shown once
```

Verifying deliveries is on [webhooks](/docs/webhooks). The dashboard's overview numbers (balance, spend by day, agent and tool, items not charged, budgets, pending approvals and disputes) are `GET /v1/orgs/{orgId}/dashboard`.

## Errors you'll meet

- `403 forbidden`: a read key tried to change something, a key was used on another org, or a member tried an owner-only action.
- `402 plan_limit`: the plan's agents, seats or webhook endpoints are used up.
- `404 not_found`: the thing isn't in this org.
- `429 rate_limited`: over 300 requests a minute.

## Every dashboard action and its endpoint

Generated from the list our tests check: every server action in the dashboard is here, every endpoint is in [/openapi.json](/openapi.json), and an org key is tested against every row marked yes.

Dashboard ↔ API

| Action | Dashboard page | Endpoint | Org key |
|---|---|---|---|
| Email me a sign-in link | `/app` | `POST /auth/email/start` | no: a person signed in |
| Sign out | `/app` | `POST /auth/logout` | no: a person signed in |
| Create an org | `/app` | `POST /v1/orgs` | no: a person signed in |
| Accept an invite | `/invite` | `POST /v1/invites/accept` | no: a person signed in |
| Balance, spend and refund rate | `/app` | `GET /v1/orgs/{orgId}/dashboard` | yes |
| List agents | `/app/agents` | `GET /v1/orgs/{orgId}/agents` | yes |
| Add an agent (key shown once) | `/app/agents` | `POST /v1/orgs/{orgId}/agents` | yes |
| Change name, budget, approval threshold, IP allowlist or status | `/app/agents` | `PATCH /v1/orgs/{orgId}/agents/{agentId}` | yes |
| Rotate an agent key | `/app/agents` | `POST /v1/orgs/{orgId}/agents/{agentId}/rotate-key` | yes |
| Revoke an agent key | `/app/agents` | `POST /v1/orgs/{orgId}/agents/{agentId}/revoke-key` | yes |
| Approval requests | `/app/approvals` | `GET /v1/orgs/{orgId}/approvals` | yes |
| Approve or reject a purchase | `/app/approvals` | `POST /v1/orgs/{orgId}/approvals/{id}/decision` | yes |
| Receipt explorer | `/app/receipts` | `GET /v1/orgs/{orgId}/receipts` | yes |
| One receipt | `/app/receipts/[id]` | `GET /v1/orgs/{orgId}/receipts/{id}` | yes |
| Export receipts as CSV | `/app/receipts` | `GET /v1/orgs/{orgId}/exports/receipts.csv` | yes |
| Dispute a charged item | `/app/receipts/[id]` | `POST /v1/orgs/{orgId}/disputes` | yes |
| Disputes and their decisions | `/app/disputes` | `GET /v1/orgs/{orgId}/disputes` | yes |
| Invoices | `/app/invoices` | `GET /v1/orgs/{orgId}/invoices` | yes |
| One invoice (JSON or printable HTML) | `/app/invoices` | `GET /v1/orgs/{orgId}/invoices/{id}` | yes |
| Balance and credit lots | `/app/billing` | `GET /v1/orgs/{orgId}/balance` | yes |
| Top-up history | `/app/billing` | `GET /v1/orgs/{orgId}/topups` | yes |
| Buy credits (returns a checkout link) | `/app/billing` | `POST /v1/orgs/{orgId}/topups` | yes |
| Auto-reload settings | `/app/billing` | `GET /v1/orgs/{orgId}/auto-reload` | yes |
| Turn auto-reload on or off | `/app/billing` | `PUT /v1/orgs/{orgId}/auto-reload` | yes |
| Low-balance alert level | `/app/billing` | `PUT /v1/orgs/{orgId}/notifications` | yes |
| Plan, limits and subscriptions | `/app/billing` | `GET /v1/orgs/{orgId}/plan` | yes |
| Start the Pro plan (returns a checkout link) | `/app/billing` | `POST /v1/orgs/{orgId}/subscriptions` | yes |
| Cancel a plan at period end | `/app/billing` | `DELETE /v1/orgs/{orgId}/subscriptions/{plan}` | yes |
| Billing name, address, country and tax ID | `/app/billing` | `PATCH /v1/orgs/{orgId}/billing` | yes |
| Webhook endpoints and event types | `/app/webhooks` | `GET /v1/orgs/{orgId}/webhooks` | yes |
| Recent deliveries | `/app/webhooks` | `GET /v1/orgs/{orgId}/webhook-deliveries` | yes |
| Recent events | `/app/webhooks` | `GET /v1/orgs/{orgId}/events` | yes |
| Add an endpoint (secret shown once) | `/app/webhooks` | `POST /v1/orgs/{orgId}/webhooks` | yes |
| Send a test event | `/app/webhooks` | `POST /v1/orgs/{orgId}/webhooks/{id}/test` | yes |
| Delete an endpoint | `/app/webhooks` | `DELETE /v1/orgs/{orgId}/webhooks/{id}` | yes |
| Members and pending invites | `/app/team` | `GET /v1/orgs/{orgId}/members` | yes |
| Invite by email | `/app/team` | `POST /v1/orgs/{orgId}/invites` | yes |
| Withdraw an invite | `/app/team` | `DELETE /v1/orgs/{orgId}/invites/{inviteId}` | yes |
| Make a member an owner, or back | `/app/team` | `PATCH /v1/orgs/{orgId}/members/{userId}` | yes |
| Remove a member, or leave | `/app/team` | `DELETE /v1/orgs/{orgId}/members/{userId}` | yes |
| Org API keys | `/app/team` | `GET /v1/orgs/{orgId}/keys` | no: a person signed in |
| Make an org API key (shown once) | `/app/team` | `POST /v1/orgs/{orgId}/keys` | no: a person signed in |
| Revoke an org API key | `/app/team` | `DELETE /v1/orgs/{orgId}/keys/{id}` | no: a person signed in |
| Data-deletion requests | `/app/team` | `GET /v1/orgs/{orgId}/deletion-requests` | yes |
| Ask us to delete stored inputs and results (within 24 hours) | `/app/team` | `POST /v1/orgs/{orgId}/deletion-requests` | no: a person signed in |
| Private test sets and included calls | `/app/benchmarks` | `GET /v1/orgs/{orgId}/test-sets` | yes |
| Private benchmark runs | `/app/benchmarks` | `GET /v1/orgs/{orgId}/benchmarks` | yes |
| One run: per-case results, segments, failure reasons | `/app/benchmarks/[id]` | `GET /v1/orgs/{orgId}/benchmarks/{id}` | yes |
| Make a private test set (JSON Lines) | `/app/benchmarks` | `POST /v1/orgs/{orgId}/test-sets` | yes |
| Add cases to a test set | `/app/benchmarks` | `POST /v1/orgs/{orgId}/test-sets/{id}/cases` | yes |
| Delete a test set and its results | `/app/benchmarks` | `DELETE /v1/orgs/{orgId}/test-sets/{id}` | yes |
| Run tools against a test set | `/app/benchmarks` | `POST /v1/orgs/{orgId}/benchmarks` | yes |
| Your tools' scores, inputs and rank (read-only) | `/app/provider` | `GET /v1/orgs/{orgId}/provider` | yes |
| Claim a provider | `/app/provider` | `POST /v1/orgs/{orgId}/provider/claims` | yes |
| Ask for a re-test (Insights, Pro) | `/app/provider` | `POST /v1/orgs/{orgId}/provider/retests` | yes |

Updated 2026-09-30. This page as HTML: https://arettic.com/docs/org-api · Markdown: https://arettic.com/docs/org-api.md · JSON: https://arettic.com/docs/org-api.json
