# API reference

Every Powerloom resource that you can model in YAML you can also create through the REST API. The REST surface is what `weave` (the CLI) and the console call; it's what you'll call from any application that needs to invoke an agent, list a fleet, or react to an audit event.

This page covers authentication, the resource shape, conventions across endpoints, and where to find the full machine-readable spec.

---

## Where the spec lives

Powerloom serves OpenAPI 3.1 at `/api/openapi.json` (or `https://api.powerloom.org/openapi.json` against the cloud edition). That document is the source of truth — every endpoint, every body, every response shape is enumerated there. Generate clients from it, point Postman at it, hand it to your tooling of choice.

This page is the human-readable orientation. For the exhaustive endpoint list, the OpenAPI doc is faster to consume.

---

## Authentication

Three ways to authenticate, in order of typical use:

### JWT (browser, console)

The console issues a short-lived JWT after OIDC SSO or password+MFA. The token rides in the `Authorization: Bearer <jwt>` header. JWTs are user-scoped — every request resolves to your principal and your effective RBAC at the time.

JWTs expire in 30 minutes. The browser refreshes them silently via the refresh-token cookie; you don't see this. For machine-to-machine use, prefer a PAT.

### Personal Access Tokens (CLI, automation)

Mint a PAT at `/settings/access-tokens`. The token format is `pat_<43 char base64>`. Copy it once at mint time — Powerloom only stores the SHA-256 hash. Revoke from the same page.

```bash
curl -H "Authorization: Bearer pat_xxxxxxxx..." \
  https://api.powerloom.org/me
```

PATs have the same RBAC as the user that minted them. Use sub-principals (next) when you want narrower scope per agent client.

### Sub-principals (per-agent identities)

For per-AI-client identity (e.g., one identity for Claude Code, another for Codex CLI), mint a sub-principal at `/settings/agents`. Each sub-principal is a distinct `principal_id` in the audit log — you see "Shane via CC" vs "Shane via Codex" instead of just "Shane."

Sub-principals can optionally have *narrowed* permissions — RBAC is intersected against the parent user's RBAC. The user is the ceiling; the sub-principal can only restrict, never widen.

Token format: `sub_<43 char base64>`. Same `Authorization: Bearer` header.

### Service accounts (CI, workload identity)

For org-owned non-human principals — CI runners, deploy bots, scheduled jobs — mint a Service Account at `/organization/service-accounts`. SA tokens look like `sa_<43 char base64>` and are governed by the same RBAC primitives as users.

---

## Conventions

### Base URL

Cloud: `https://api.powerloom.org`. Self-hosted: whatever your container is bound to (`http://localhost:8000` for `docker compose up` defaults).

### Content type

JSON for everything. `Content-Type: application/json` on requests with a body. Responses always set the header.

### Pagination

List endpoints use `?limit=` and `?cursor=` (cursor-based; the response includes `next_cursor` when more pages are available).

```
GET /agents?limit=50&cursor=eyJpZCI6...
```

Default limit is 50. Max is 200.

### Error shape

Every non-2xx response carries this body shape:

```json
{
  "error": {
    "code": "rbac_denied",
    "message": "You do not have AgentAuthor at /acme/production.",
    "request_id": "req_01ABCDE..."
  }
}
```

`code` is machine-readable; `message` is human-readable. `request_id` matches the value in the `X-Request-ID` response header — quote it when filing a support issue.

### Idempotency

Mutating endpoints accept an optional `Idempotency-Key` header. Same key + same body within 24h returns the original response without re-executing. Useful for retry-safe machine clients.

---

## Resource endpoints

Every Kind in [/docs/manifest-schema](/docs/manifest-schema) has a parallel REST surface:

| Kind | List | Create | Read | Update | Delete |
|---|---|---|---|---|---|
| OU | `GET /ous` | `POST /ous` | `GET /ous/{id}` | `PATCH /ous/{id}` | `DELETE /ous/{id}` |
| Group | `GET /groups` | `POST /groups` | `GET /groups/{id}` | `PATCH /groups/{id}` | `DELETE /groups/{id}` |
| RoleBinding | `GET /role-bindings` | `POST /role-bindings` | `GET /role-bindings/{id}` | — | `DELETE /role-bindings/{id}` |
| Agent | `GET /agents` | `POST /agents` | `GET /agents/{id}` | `PATCH /agents/{id}` | `DELETE /agents/{id}` |
| Skill | `GET /skills` | `POST /skills` | `GET /skills/{id}` | `PATCH /skills/{id}` | `DELETE /skills/{id}` |
| MCPDeployment | `GET /mcp-deployments` | `POST /mcp-deployments` | `GET /mcp-deployments/{id}` | `PATCH /mcp-deployments/{id}` | `DELETE /mcp-deployments/{id}` |
| Credential | `GET /credentials` | `POST /credentials` | `GET /credentials/{id}` | — | `DELETE /credentials/{id}` |

Role bindings and credentials don't expose `PATCH` — to change them, delete + recreate. This is intentional; it means the audit log captures discrete grant + revoke events instead of mutable rules that drift in meaning.

---

## Agent invocation

The endpoint that does work, as opposed to the endpoints that govern who can do work.

```
POST /agents/{id}/invoke
{
  "messages": [
    {"role": "user", "content": "Summarize the latest support tickets."}
  ],
  "stream": false
}
```

Response (non-streaming) carries the full assistant reply, the tool-use trace, and the session id for follow-up turns.

For streaming, set `stream: true` and consume the `text/event-stream` response — events are `agent.text_delta`, `agent.tool_use_start`, `agent.tool_use_end`, `agent.complete`. Same shape the console's chat surface consumes.

For a multi-turn conversation, pass `session_id` from the prior response on the next request — Powerloom stitches the turns together server-side, including memory retrieval and any approval-pending pauses.

---

## Audit log

Every state-changing action lands in the audit log; you can query it.

```
GET /audit?action_verb=create&resource_kind=agent&since=2026-04-01
```

Filters: `action_verb`, `resource_kind`, `resource_id`, `actor_principal_id`, `since`, `until`. All optional; an unfiltered request returns the most recent 50 events.

For an export, `GET /audit/export?format=csv` returns a streaming CSV with the same filters applied. Large exports stream over many minutes — keep the connection open.

---

## Notifications

The Phase 34 surface — list your unread, mark read, manage preferences, mint push subscriptions.

```
GET    /me/notifications
GET    /me/notifications/unread-count
POST   /me/notifications/{id}/read
GET    /me/notification-preferences
PUT    /me/notification-preferences/{kind}
POST   /me/push-subscriptions
GET    /push/vapid-public-key      # public, no auth
```

The notification stream `GET /me/notifications/stream` is server-sent events; consume with the standard EventSource API.

---

## What this surface doesn't do

The REST API doesn't proxy LLM calls. When you `POST /agents/{id}/invoke`, Powerloom calls the upstream runtime (Anthropic by default) directly with your encrypted credential — we don't sit between you and Anthropic, we just govern who's allowed to make the call.

The REST API doesn't expose internal compliance machinery (audit chain hashing, work-chain events, reconciler internals) on the public surface. Those live behind the `/_admin/` prefix and require super-admin auth, which is gated to the Powerloom-side super-admin team only.
