リファレンスHTTP APIPush requests

Approve a push request

POST
/v1/sessions/{id}/push-requests/{push_request_id}/approve

Approves a requested push request: the platform pushes the pinned commit to the request's branch. git_commit must be the commit the approver reviewed; an approval naming any other is refused. An approval is also refused when the session has checkpointed since the request was made, so what is approved is what the session last committed; reject the request and ask again.

Only a verified credential that holds sessions:approve approves or rejects: an API key or a signed-in user. A workspace key (personal or agent) that started the session never approves or rejects its requests (403, refusal: self_approval); another key or a person does. Worker credentials are not accepted on /v1 at all (401). Every attempt, refused ones included, leaves a resolve_push receipt carrying the actor, the verdict and the refusal reason; a refusal's error.details.receipt_id names it. A request is approved or rejected once: a second answer, by either path, is 409 PUSH_REQUEST_RESOLVED. An Idempotency-Key is shared with /reject: replayed on either with the same answer and body, it answers the request as it is now (once the platform has taken it to push, state reads pushing, pushed or failed, while resolved_by, resolved_at and resolution_receipt_id still name this answer); with another answer or body it is 409 IDEMPOTENCY_CONFLICT.

Scope: sessions:approve (API key or cookie session). Idempotency-Key: required.

Authorization

headerAuthorizationBearer <token>

For servers and scripts: an API key sent as an HTTP bearer token, Bearer <key>. It holds exactly the scopes it was issued with; a key issued before scopes existed holds none. It never needs the CSRF header, and it cannot manage agents, members, teams or API keys. Issue one in the web console under Settings → API keys; each operation names the scope it needs.

Scope: sessions:approve

Path Parameters

id*string

The session's id.

Match^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$
Formatuuid
push_request_id*string

The push request's id.

Match^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$
Formatuuid

Header Parameters

Idempotency-Key*string

Required, 1–255 characters; a UUID per user intent works. A retry with the same key and the same body returns the original response and runs the command once; the same key with a different body answers 409 IDEMPOTENCY_CONFLICT. A missing or invalid key answers 400 BAD_REQUEST, after the body is read.

Length1 <= length <= 255
X-Requested-With?"werft-web"

Required with a cookie session: the literal werft-web.

Value in"werft-web"

Request Body

application/json
  1. body
git_commit*string
Match^[0-9a-f]{40}$
reason?string
Length1 <= length <= 1000

Response Body

The request as answered.

application/json
  1. response
id*string
Match^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$
Formatuuid
session_id*string
Match^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$
Formatuuid
repository_id*string
Length1 <= length
checkpoint_revision*integer
Range0 <= value <= 2147483647
git_commit*string
Match^[0-9a-f]{40}$
target_branch*string
Length1 <= length
state*string
Value in"requested""approved""rejected""pushing""pushed""failed"
requested_by*
requested_at*string
Match^(?:(?:\d\d[2468][048]|\d\d[13579][26]|\d\d0[48]|[02468][048]00|[13579][26]00)-02-29|\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\d|30)|(?:02)-(?:0[1-9]|1\d|2[0-8])))T(?:(?:[01]\d|2[0-3]):[0-5]\d:[0-5]\d(?:\.\d+)?(?:Z))$
Formatdate-time
resolved_by*||null
resolved_at*|null
resolution_reason*string|null
request_receipt_id*string
Match^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$
Formatuuid
resolution_receipt_id*|null
finished_at*|null
push_receipt_id*|null
origin*string
Default"direct"
Value in"direct""intent"
flow*string
Default"legacy"
Value in"legacy""actions"
pr_state*|
Defaultnull
pr_error*|
Defaultnull
pr_number*|
Defaultnull
pr_url*|
Defaultnull
Example Request
curl -X POST 'https://example.com/v1/sessions/497f6eca-6276-4993-bfeb-53cbbbba6f08/push-requests/497f6eca-6276-4993-bfeb-53cbbbba6f08/approve' \  -H 'Idempotency-Key: string' \  -H 'Content-Type: application/json' \  -d '{  "git_commit": "4f2a9c1e7b3d5a8f0c6e2b9d1a4f7c3e5b8d0a2c"}'
Example Response
{  "id": "5b9e2c17-4f3a-4d6e-8a1b-7c0d9e2f3a41",  "session_id": "0f8b6a52-3d1e-4c7a-9b2f-5e6d7c8a9b01",  "repository_id": "sample-app",  "checkpoint_revision": 3,  "git_commit": "4f2a9c1e7b3d5a8f0c6e2b9d1a4f7c3e5b8d0a2c",  "target_branch": "agent/0f8b6a52-3d1e-4c7a-9b2f-5e6d7c8a9b01",  "state": "approved",  "requested_by": {    "principal": {      "kind": "user",      "id": "3e7f1a90-5c2d-4b8e-8f61-2a9d0c4b7e35"    },    "actor_user_id": "3e7f1a90-5c2d-4b8e-8f61-2a9d0c4b7e35",    "agent_id": null  },  "requested_at": "2026-10-11T08:30:00Z",  "resolved_by": {    "principal": {      "kind": "user",      "id": "3e7f1a90-5c2d-4b8e-8f61-2a9d0c4b7e35"    },    "actor_user_id": "3e7f1a90-5c2d-4b8e-8f61-2a9d0c4b7e35",    "agent_id": null  },  "resolved_at": "2026-10-11T08:35:00Z",  "resolution_reason": null,  "request_receipt_id": "c4a1e9d2-7b3f-4e8a-a6c5-1d2e3f4a5b6c",  "resolution_receipt_id": "e1d2c3b4-a5f6-4789-9abc-def012345678",  "finished_at": null,  "push_receipt_id": null}