---
title: "Late-bind a session pubkey to an envelope that was created without one"
method: POST
path: "/api/envelopes/{envelope_id}/session-bind"
---

# Late-bind a session pubkey to an envelope that was created without one

`POST /api/envelopes/{envelope_id}/session-bind`

## Request

- Base URL: `https://free-sign.com`
- URL: `https://free-sign.com/api/envelopes/{envelope_id}/session-bind`
- Auth: none declared

MCP `create_signing_envelope` creates envelopes whose `session_pubkey_jwk_json` is NULL (an AI agent has no IndexedDB to keep a private key in). The first browser to open the resulting `signing_url` generates a non-extractable ECDSA P-256 keypair and POSTs the public JWK here. The handler UPDATEs the envelope row iff `session_pubkey_jwk_json IS NULL` (write-once, enforced by both the WHERE clause AND by trg_envelopes_session_update from migrations 0010 + 0011). Subsequent protected requests then carry the standard `x-fsig-session-*` headers. NOT required when the envelope was created via `POST /api/envelopes` directly — that call already binds the session.

## Path parameters

- `envelope_id` string, required

## Request body

- object
  - `session_pubkey_jwk` object, required: ECDSA P-256 public key in JWK format. Must be kty=EC, crv=P-256, with x and y as 43-char base64url (32-byte coordinates). Must NOT contain the private `d` field.

## Response `200`

Session bound.

- object
  - `envelope_id` string, required
  - `session_pubkey_sha256` string, required
  - `session_bound_at` string, date-time, required

## Other responses

- `400`: invalid_session_pubkey_jwk.
- `404`: not_found — envelope does not exist.
- `409`: session_already_bound — another browser already bound this envelope.

---

[API](https://skmtc.dev/free-sign/apis/freesign-free-e-signature-api.md) · [All operations](https://skmtc.dev/free-sign/apis/freesign-free-e-signature-api/llms.txt) · [OpenAPI document](https://skmtc.dev/free-sign/apis/freesign-free-e-signature-api/revisions/5d8e9854ab85?raw)
