# Bind a device

Register a device key through an AuthenSee ceremony.


**Auth:** Two matching one-time bind proofs.
## Request and response

Angle-bracket values describe placeholders, including nested objects and numeric types; these are wire shapes, not captured service data. Replace them before sending.

```http
POST /v1/graph-keys/bind/<bind-id>/finalize HTTP/1.1
Host: trust-staging.rebellion.systems
Content-Type: application/json

{
  "initiationProof": "<gbi-proof>",
  "completionProof": "<gbc-proof>"
}
```

Response shape:

```json role="response"
{
  "status": "complete",
  "graphKey": "<completed-graph-key-object>"
}
```




Binding associates a P-256 public key with an AuthenSee-authenticated persona. Generate and retain the private key on the device. A bearer credential cannot replace this ceremony.

## Start

`POST /v1/graph-keys/bind/start`

| Field | Requirement |
| --- | --- |
| `publicKeyJwk` | Required public P-256 JWK |
| `appOrigin` | Exact allowlisted application origin, including scheme and port |
| `nodeKind` | `human` (default) or `agent` |
| `label` | Optional device label, at most 200 characters |
| `metadata` | Optional JSON object; identity-routing fields are not accepted |
| `ttlSeconds` | Optional integer, 300–604800; device lifetime |
| `recoverNodeId` | Optional existing 64-character lowercase hex node ID |
| `recoverByPersona` | Optional boolean persona recovery selector |
| `purpose` | `graph` or `content-signing`; content signing needs a dedicated fresh key and `recoverNodeId` |
| `nativeReturn` | Optional boolean; requires an origin with configured native return support |

Use the SDK's recovery flow to select `recoverNodeId` or `recoverByPersona`; do not initiate an unrelated first bind to recover an account. Incompatible recovery combinations are rejected. Caller-selected provider identities and the removed `deviceAdd` field are rejected.

```http
POST /v1/graph-keys/bind/start
Content-Type: application/json

{
  "publicKeyJwk": { "kty": "EC", "crv": "P-256", "x": "public-x", "y": "public-y" },
  "appOrigin": "https://your-app.example",
  "nodeKind": "human"
}
```

The response supplies `bindId`, `graphKeyId`, `graphNodeId`, `hostedUrl`, `expiresAt` and `initiationProof`. Persist the initiation proof, intended public key, exact origins and expiry before opening `hostedUrl`. The start response's node ID is provisional; the completed key supplies the authoritative node.

## Complete

After the person authenticates, `GET /v1/authensee/callback` exchanges the provider result and returns the completion proof through the configured relay. The web return path is `/trust-graph/bind/callback`; `bindId` and `completionProof` arrive in its fragment. Remove the fragment promptly and match the saved bind and origins.

`POST /v1/graph-keys/bind/:bindId/finalize` accepts:

```json role="request"
{
  "initiationProof": "gbi_one-time-proof-from-start",
  "completionProof": "gbc_one-time-proof-from-callback"
}
```

Successful completion returns `{status: "complete", graphKey}`. Both proofs must match the same unexpired bind. For a first bind with device-chain creation required, also supply `root: {record, signature}`. Use the SDK bind helpers to construct and persist that prospective root and handle supported root corrections.

`GET /v1/graph-keys/bind/:bindId` returns `status`, `expiresAt` and, when complete, `graphKey`. Status is `pending`, `exchanged`, `complete`, `expired` or `failed`. Polling does not reveal either proof. Match a recovered completion to the public key and node saved in the pending intent.


**Never**
Keep both proofs private. A completed bind is not device approval: refresh and verify the device directory before granting graph access.

## Recovery and errors

Fresh recovery devices on chained nodes start quarantined. Existing expired graph-purpose keys can use the ceremony to refresh their binding when the authenticated persona matches; revoked keys cannot be revived. Ordinary active-key rebinds return `GRAPH_KEY_REBIND_NOT_DUE`, except where the independent authentication horizon makes reauthentication eligible.

| Response | Meaning |
| --- | --- |
| 403 `GRAPH_RECOVERY_NOT_FOUND` | Recovery found no existing account |
| 403 `GRAPH_BIND_SESSION_MISMATCH` / `GRAPH_BIND_ACCOUNT_MISMATCH` | Provider result does not match the pending ceremony |
| 403 `GRAPH_BIND_PERSONA_FORBIDDEN` | Authenticated persona does not own the selected node |
| 409 `GRAPH_KEY_REVOKED` | Same-key recovery cannot revive this key |
| 400 `BIND_ROOT_REQUIRED` | First-bind root is required |
| 400 `BIND_ROOT_UNSUPPORTED` | Chain creation is disabled |
| 409 `BIND_ROOT_UNEXPECTED` | Ceremony resolved to an existing node |
| 409 `BIND_RECEIPT_MISMATCH` | Retry differs from the committed bind receipt |
| 503 `BIND_IDENTITY_UNAVAILABLE` | Identity routing is temporarily unavailable |

Recovery starts have a separate per-reference budget, with 429 and `retry-after`. Callback errors are HTML with the corresponding HTTP status, rather than the JSON format used by bind API calls.


**Does not establish**
This ceremony is scoped to its initiating client and application origin. A forwarded hosted URL is not a supported cross-device binding flow. The `human` or `agent` classification does not independently establish humanhood or delegated authority.
