# REST API

A conventional, versioned REST API over the same agent keys as MCP. Every MCP tool has a REST endpoint, so you can drive ContextOwl from any HTTP client without an MCP runtime. Same `cowl_pat_` bearer key, same permission model (permission + role ceiling + workspace scope + plan), same audit trail and outbound webhooks.

## Quick start

1. Create an agent key in **Admin > Settings > API**. It starts with `cowl_pat_` and is shown once, so copy it into your secret manager.
2. Pick the workspace id you want to work in (or use `-` with a workspace-bound key).
3. Call the API with the key as a bearer token:

```bash:first-call.sh
curl -s "https://contextowl.co/api/v1/workspaces/platform/articles" \
  -H "Authorization: Bearer cowl_pat_YOUR_KEY"
```

That returns the workspace's articles as JSON. Every other endpoint follows the same shape: a bearer key, a workspace in the path, JSON in and out.

## Run against a local instance

Self-hosting or hacking on ContextOwl? Run the stack and point the API at localhost.

```bash:run-local.sh
git clone https://github.com/bjarneo/context360.git
cd context360
make dev   # Postgres :5433, backend :8080, Vite :5173 (proxies /api)
```

Sign in at http://localhost:5173, create a key under **Admin > Settings > API**, then call the local API. The seeded workspace id is `prod`:

```bash:local-call.sh
# search the local instance
curl -s "http://localhost:8080/api/v1/workspaces/prod/search?q=auth" \
  -H "Authorization: Bearer cowl_pat_YOUR_KEY"

# fetch the local OpenAPI spec
curl -s http://localhost:8080/api/v1/openapi.json | head
```

## Base URL

```text
https://contextowl.co/api/v1
```

Authenticate every request with your agent key:

```text
Authorization: Bearer cowl_pat_YOUR_KEY
```

The `{workspace}` path segment is a workspace id. A workspace-bound key may use `-` to mean its bound workspace; an org-wide key must name one.

## OpenAPI spec

The full machine-readable contract is served (and powers the API Reference pages in this workspace):

```text
https://developers.contextowl.co/api/v1/openapi.json
```

## Search

```bash:search.sh
curl -s "https://contextowl.co/api/v1/workspaces/platform/search?q=agent%20keys" \
  -H "Authorization: Bearer cowl_pat_YOUR_KEY"
```

Add `&semantic=true` for embedding-ranked results (falls back to full-text search).

## Read an article

```bash:get.sh
curl -s https://contextowl.co/api/v1/workspaces/platform/articles/mcp \
  -H "Authorization: Bearer cowl_pat_YOUR_KEY"
```

## Create a draft

```bash:create.sh
curl -s -X POST https://contextowl.co/api/v1/workspaces/platform/articles \
  -H "Authorization: Bearer cowl_pat_YOUR_KEY" \
  -H "Content-Type: application/json" \
  -d '{"title":"Agent Draft","section":"Guides","markdown":"# Agent Draft\n\nDrafted over the REST API."}'
```

## Publish an article

Changing status needs both article.update and article.publish.

```bash:publish.sh
curl -s -X PATCH https://contextowl.co/api/v1/workspaces/platform/articles/agent-draft \
  -H "Authorization: Bearer cowl_pat_YOUR_KEY" \
  -H "Content-Type: application/json" \
  -d '{"status":"STABLE"}'
```

## Errors

Errors use the standard envelope with an HTTP status:

```json
{"error":{"code":"permission_denied","message":"this key lacks the required permission","status":403}}
```

Common codes: 401 unauthorized (missing or invalid key), 403 permission_denied or plan_required, 404 not_found (also cross-org workspaces, which never leak existence), 400 invalid_request, 429 rate_limited.
