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

# Authentication & Signing

> How to authenticate requests and generate request signatures.

## Auth Requirements By Endpoint Type

* `GET /external-api/...`: `Authorization: Bearer <org_api_key>`
* `PATCH` and `POST` write requests under `/external-api/...`: `Authorization` + `X-Cora-Timestamp` + `X-Cora-Signature`

## API Key Format

Expected bearer token format:

```text theme={null}
cora_org_<keyId>.<secret>
```

Use the full value in `Authorization`. The `<secret>` portion is used for HMAC signing.

## Timestamp Window

* `X-Cora-Timestamp` must be within 5 minutes of server time.
* Seconds or milliseconds are accepted.

## Signature Algorithm

For each signed JSON write request:

1. Build `rawBody` as the exact JSON string sent.
2. Compute `bodyHash = sha256(rawBody)` as hex.
3. Build canonical payload:

```text theme={null}
<timestamp>.<METHOD_UPPER>.<path_with_query>.<bodyHash>
```

4. Compute signature:

```text theme={null}
HMAC_SHA256_HEX(secret, canonical_payload)
```

5. Send it as `X-Cora-Signature`.

`path_with_query` must match the actual request path exactly.

Examples:

* `PATCH /external-api/accounts/FILE_123`
* `POST /external-api/accounts/bulk-upsert`

## Node.js Signing Example

```ts theme={null}
import crypto from "crypto";

function sha256Hex(value: string) {
  return crypto.createHash("sha256").update(value).digest("hex");
}

function hmacHex(secret: string, payload: string) {
  return crypto.createHmac("sha256", secret).update(payload).digest("hex");
}

function parseApiKey(apiKey: string) {
  const token = apiKey.startsWith("cora_org_") ? apiKey.slice("cora_org_".length) : apiKey;
  const dot = token.indexOf(".");
  if (dot < 0) throw new Error("Invalid API key format");
  return {
    keyId: token.slice(0, dot),
    secret: token.slice(dot + 1),
  };
}

function buildSignedJsonHeaders(args: {
  apiKey: string;
  secret: string;
  method: "PATCH" | "POST";
  pathWithQuery: string;
  rawBody: string;
}) {
  const timestamp = Math.floor(Date.now() / 1000).toString();
  const canonical = `${timestamp}.${args.method}.${args.pathWithQuery}.${sha256Hex(args.rawBody)}`;
  const signature = hmacHex(args.secret, canonical);

  return {
    Authorization: `Bearer ${args.apiKey}`,
    "X-Cora-Timestamp": timestamp,
    "X-Cora-Signature": signature,
    "Content-Type": "application/json",
  };
}
```

## Auth Error Codes

* `MISSING_AUTH_HEADER`: Missing `Authorization` on API-key-auth endpoint
* `MISSING_AUTH_HEADERS`: Missing one or more required signed request auth headers
* `INVALID_API_KEY`: Key invalid, revoked, or not found
* `REQUEST_TIMESTAMP_OUTSIDE_WINDOW`: Timestamp outside allowed skew
* `INVALID_REQUEST_SIGNATURE`: Signature does not match request payload
* `API_KEY_ORG_MISMATCH`: Key does not belong to `:organizationId`
* `AUTH_CHECK_FAILED`: Internal auth verification failure
