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

# Machine access

> Authenticate a backend service with PymtHouse using the OAuth 2.0 client credentials grant or HTTP Basic auth.

Machine access is the authentication pattern for server-to-server calls: your backend presents its M2M client credentials directly to PymtHouse without user involvement. This follows the **OAuth 2.0 client credentials grant** (RFC 6749 §4.4) and the **HTTP Basic authentication scheme** (RFC 7617) for Builder API calls.

## When to use machine access

Use this pattern when your backend needs to:

* Provision or update users via the Builder API.
* Mint user-scoped JWTs for end-users already known to your system.
* Read aggregated usage data from the Usage API.
* Complete a device authorization on behalf of a user (RFC 8693 token exchange).

Do **not** use this pattern for interactive user login flows. For those, see [Interactive login](/integration/interactive-login) or [Device flow](/integration/device-flow).

## Prerequisites

* A confidential M2M client (`m2m_…` id and `pmth_cs_…` secret). See [Client model](/integration/client-model).
* The M2M client has been granted the scopes required by the endpoint you are calling.

```bash theme={null}
export BASE_URL="https://your-pymthouse.example"
export M2M_ID="m2m_yourClientId"
export M2M_SECRET="pmth_cs_yourSecret"
```

## Option A: Client credentials grant

Exchange your credentials for a short-lived Bearer token, then use that token for subsequent API calls. This is the preferred pattern when making multiple API calls in the same request lifecycle, because it decouples token acquisition from the API call.

### 1. Obtain a machine token

The token endpoint is published in OIDC discovery under `token_endpoint`. For convenience it is stable at `{issuer}/token`.

```bash theme={null}
MACHINE_TOKEN=$(curl -sS \
  -H "Content-Type: application/x-www-form-urlencoded" \
  -d "grant_type=client_credentials" \
  -d "client_id=${M2M_ID}" \
  -d "client_secret=${M2M_SECRET}" \
  -d "scope=users:write users:token" \
  "${BASE_URL}/api/v1/oidc/token" | jq -r '.access_token')
```

**Scope selection.** Request only the scopes your call sequence requires. The full list of available M2M scopes is in [Client model — M2M scopes](/integration/client-model#m2m-client-scopes).

**Token lifetime.** Machine tokens are short-lived. Cache and reuse the token for the duration of a request batch, then discard it. Do not persist machine tokens across application restarts — just acquire a new one.

### 2. Call the Builder API with Bearer auth

```bash theme={null}
curl -sS \
  -H "Authorization: Bearer ${MACHINE_TOKEN}" \
  -H "Content-Type: application/json" \
  -d '{ "externalUserId": "user-123", "email": "alice@example.com", "status": "active" }' \
  "${BASE_URL}/api/v1/apps/${PUBLIC_CLIENT_ID}/users"
```

## Option B: HTTP Basic auth

For single calls where acquiring a separate token adds unnecessary latency, pass the M2M credentials directly using HTTP Basic auth (RFC 7617). PymtHouse accepts Basic auth on all Builder API endpoints.

```bash theme={null}
curl -sS \
  -u "${M2M_ID}:${M2M_SECRET}" \
  -H "Content-Type: application/json" \
  -d '{ "externalUserId": "user-123", "email": "alice@example.com", "status": "active" }' \
  "${BASE_URL}/api/v1/apps/${PUBLIC_CLIENT_ID}/users"
```

The tenant boundary is enforced identically in both authentication modes: the `clientId` in the URL path must match the authenticated confidential client's associated app. A valid credential from a different app's M2M client returns `404`.

## Choosing between Bearer and Basic

|                         | Client credentials (Bearer)      | HTTP Basic                   |
| ----------------------- | -------------------------------- | ---------------------------- |
| **Round trips**         | 2 (token + API)                  | 1                            |
| **Best for**            | Batching multiple API calls      | Single, isolated calls       |
| **Credential exposure** | Token (short-lived) per API call | Raw credentials per API call |
| **Standard**            | RFC 6749 §4.4                    | RFC 7617                     |

For high-throughput backends making many calls in sequence, acquire one token and reuse it for the batch. For low-frequency automations and operational scripts, Basic auth is simpler.

## Error responses

| Status             | Condition                                                                             |
| ------------------ | ------------------------------------------------------------------------------------- |
| `400 Bad Request`  | Malformed token request body, unknown `grant_type`, or unsupported `scope`.           |
| `401 Unauthorized` | Invalid `client_id`, wrong `client_secret`, or expired Bearer token.                  |
| `403 Forbidden`    | Valid credentials, but the client does not have the required scope for this endpoint. |
| `404 Not Found`    | Valid credentials, but the M2M client does not belong to the app in the URL path.     |

## Rotating client secrets

Rotate M2M secrets through the app credentials endpoint in the developer dashboard or admin API. After rotation:

1. Update the secret in your backend's secret manager.
2. Redeploy or restart the service.
3. Do **not** rotate the public client secret — public clients must remain secretless.

## Key design decisions

1. **Basic auth is supported alongside Bearer.** Confidential server-to-server clients are common in automation tooling where adding a token exchange step adds operational friction. Supporting both modes simplifies bootstrapping and scripting without compromising security, since the credential type (M2M secret) carries the same privilege either way.
2. **Tenant boundary on path, not query parameter.** Enforcing `clientId` as a URL path segment rather than a query parameter makes the tenant scope visible and cache-key-safe. The route handler resolves the OAuth `client_id` to an internal record before any query, keeping the public API free of internal IDs.
3. **Short-lived machine tokens, not long-lived API keys.** Using the standard client credentials flow means PymtHouse does not need a separate API-key issuance system. Short lifetimes limit the blast radius of a compromised token without requiring explicit revocation infrastructure.

## Implementation tasks

* Store `M2M_ID` and `M2M_SECRET` in your backend secret manager (e.g., HashiCorp Vault, AWS Secrets Manager, Vercel environment variables). Do not commit them to source control.
* Implement a simple in-process token cache: acquire a machine token once per request batch, reuse it, and let it expire naturally rather than building explicit refresh logic.
* For HTTP Basic auth calls, ensure your HTTP client encodes the credentials correctly (`base64(client_id + ":" + client_secret)`). Most libraries handle this via a `auth` or `user`/`password` field.
* Test that Basic auth calls to a different app's `clientId` return `404`, not `403` — this verifies that the tenant boundary is enforced, not just that the credentials are valid.
