# Authentication

Bearer keys, required headers, and key verification.

Canonical HTML: https://developer.shelfcycle.com/guides/authentication

Authenticate with a bearer API key, then verify the org, contract version, scopes, acting user, and effective capabilities before running a workflow.

The API key is a secret in the form `sk_...`. It is shown once when issued. Store it outside source control and read it from the environment.

## Base URL

Production:

```text
https://app.shelfcycle.com/api/v1
```

Local development:

```text
http://localhost:3000/api/v1
```

The route major remains `/api/v1`. The v1.21 contract is reported by `GET /me` and the OpenAPI document as `apiContractVersion` or `info.version`.

## Required header

Every API request uses the bearer token header.

```text
Authorization: Bearer <SHELFCYCLE_API_KEY>
```

Recommended client metadata:

```text
X-ShelfCycle-Client: Email notes automation
X-ShelfCycle-Request-Source: local-agent
```

## Verify with /me

`GET /me` is the first request a workflow should make. It confirms the key, tenant, contract, scopes, acting user, documentation links, and effective capabilities after live RBAC.

```bash
curl "$SHELFCYCLE_API_BASE_URL/me" \
  -H "Authorization: Bearer $SHELFCYCLE_API_KEY" \
  -H "X-ShelfCycle-Client: Email notes automation"
```

```json
{
  "data": {
    "apiVersion": "v1",
    "apiContractVersion": "v1.21",
    "org": { "id": "org-id", "name": "Northstar Chemical" },
    "key": {
      "id": "api-key-id",
      "name": "Email notes automation",
      "purpose": "Workflow for creating notes from source emails",
      "preview": "sk_...abcd",
      "scopes": ["company-reference:read", "search:read", "notes:read", "notes:write", "customers:read"],
      "accessMode": "restricted",
      "storedGrants": ["company-reference:read", "search:read", "notes:read", "notes:write", "customers:read"],
      "effectiveScopes": ["company-reference:read", "search:read", "notes:read", "notes:write", "customers:read"],
      "retiredScopes": [],
      "expiresAt": null
    },
    "actor": { "type": "user", "id": "user-id", "displayName": "Jordan Buyer" },
    "documentation": {
      "openapiJsonUrl": "https://app.shelfcycle.com/api/v1/openapi.json",
      "llmsTxtUrl": "https://app.shelfcycle.com/llms.txt"
    },
    "effectiveCapabilities": {
      "search": { "viewableTypes": ["customer", "contact", "product", "order", "purchase_order", "location"] },
      "notes": { "create": true, "view": true, "update": true, "viewableSubjectTypes": ["customer", "product"] },
      "contacts": {
        "create": false,
        "view": false,
        "update": false,
        "customer": { "create": false, "view": false, "update": false },
        "supplier": { "create": false, "view": false, "update": false }
      },
      "customers": {
        "create": false,
        "view": true,
        "update": false,
        "locations": { "view": true, "create": false, "update": false },
        "addresses": { "view": true, "create": false, "update": false },
        "documents": { "view": true, "create": false }
      },
      "suppliers": {
        "create": false,
        "view": false,
        "update": false,
        "shipFromLocations": { "view": false, "create": false, "update": false },
        "documents": { "view": false, "create": false }
      },
      "companyReference": { "activeDirectory": true, "customers": true, "suppliers": false },
      "products": { "view": true, "create": false, "update": false, "documents": { "view": true, "create": false } },
      "productFamilies": { "view": true, "create": false, "update": false },
      "costBookEntries": { "view": false, "create": false },
      "orders": { "view": false, "changedRecordDiscovery": false, "commercialEconomics": false },
      "purchaseOrders": { "view": false, "changedRecordDiscovery": false, "commercialEconomics": false },
      "reports": { "available": [] },
      "writeReadiness": {
        "notes": { "create": true, "update": true },
        "contacts": {
          "create": false,
          "update": false,
          "customer": { "create": false, "update": false },
          "supplier": { "create": false, "update": false }
        },
        "customers": {
          "create": false,
          "update": false,
          "locations": { "create": false, "update": false },
          "addresses": { "create": false, "update": false },
          "documents": { "create": false }
        },
        "suppliers": {
          "create": false,
          "update": false,
          "shipFromLocations": { "create": false, "update": false },
          "documents": { "create": false }
        },
        "products": { "create": false, "update": false, "documents": { "create": false } },
        "productFamilies": { "create": false, "update": false },
        "costBookEntries": { "create": false }
      }
    }
  }
}
```

> **Guardrail**: If the org, actor, contract version, scopes, or effective capabilities are not what the workflow expects, stop before any search or write.

## Key lifecycle errors

Authentication fails closed. These errors mean the caller should stop and request a new key or a permission change.

| Code | Meaning |
| --- | --- |
| `invalid_api_key` | Missing, malformed, unknown, or ambiguous bearer token. |
| `api_key_revoked` | The key was revoked. |
| `api_key_expired` | The key is past its expiry. |
| `api_actor_inactive` | The issued-to user no longer belongs to the org. |

## Handling secrets

Never paste the key into a coding agent prompt, Slack, email, logs, or source control. A local agent should read `SHELFCYCLE_API_KEY` from `.env` and redact the key from all output.
