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

# Authentication

> Shared bearer secret auth and callback signature verification.

# Authentication

All public Phonefarm endpoints currently use one shared bearer secret.

## Request Authentication

Send:

```http theme={null}
Authorization: Bearer <shared secret>
```

In the current implementation, this shared secret is configured as `INGEST_SECRET` in the runtime environment, even for non-ingest APIs.

For docs and integrations, treat it as one shared public API bearer secret.

Workspace-scoped API keys are part of the target architecture, but they are not the public authentication model for the documented endpoints until those endpoints are added to these docs and `openapi.json`.

## Authenticated Endpoints

* `POST /capability-requests/enqueue`
* `POST /maintenance/requests`
* `GET /processes/{process_id}`
* `POST /processes/{process_id}/cancel`
* `POST /ingest`
* `POST /cancel`
* `POST /runtime-snapshots/upsert`

## Example

```bash theme={null}
curl "$PHONEFARM_BASE_URL/processes/$PROCESS_ID" \
  -H "Authorization: Bearer $PHONEFARM_API_KEY"
```

## Callback Signing

If `PHONE_FARM_WEBHOOK_SECRET` is configured, Phonefarm signs outbound callbacks with:

* `X-PhoneFarm-Timestamp`
* `X-PhoneFarm-Signature`

Signature input:

```text theme={null}
${timestamp}.${rawJsonBody}
```

Signature algorithm:

```text theme={null}
HMAC-SHA256
```

## Example Verification

```js theme={null}
import crypto from "node:crypto";

function verifyPhonefarmSignature({ timestamp, rawBody, signature, secret }) {
  const expected = crypto
    .createHmac("sha256", secret)
    .update(`${timestamp}.${rawBody}`)
    .digest("hex");

  return crypto.timingSafeEqual(
    Buffer.from(expected, "utf8"),
    Buffer.from(signature, "utf8"),
  );
}
```

## Failure Behavior

* Missing auth header returns `401`.
* Invalid bearer token returns `401`.
* Missing server-side secret configuration returns `500`.

## Read Next

* [Webhooks](../guides/webhooks)
* [Capability Requests](./capability-requests)
