curl --request POST \
--url https://api.usepatchwork.co/v1/loom/runs/{run_id}/confirm \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"tool_call_id": "9b1deb4d-3b7d-4bad-9bdd-2b0d7b3dcb6d",
"decision": "approve"
}
'const options = {
method: 'POST',
headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'},
body: JSON.stringify({tool_call_id: '9b1deb4d-3b7d-4bad-9bdd-2b0d7b3dcb6d', decision: 'approve'})
};
fetch('https://api.usepatchwork.co/v1/loom/runs/{run_id}/confirm', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));import requests
url = "https://api.usepatchwork.co/v1/loom/runs/{run_id}/confirm"
payload = {
"tool_call_id": "9b1deb4d-3b7d-4bad-9bdd-2b0d7b3dcb6d",
"decision": "approve"
}
headers = {
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text){
"status": "success",
"data": {
"run_id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"state": "running"
}
}{
"status": "error",
"error": {
"message": "<string>",
"code": "<string>",
"details": "<unknown>"
}
}{
"status": "error",
"error": {
"message": "<string>",
"code": "<string>",
"details": "<unknown>"
}
}{
"status": "error",
"error": {
"message": "<string>",
"code": "<string>",
"details": "<unknown>"
}
}Approve or reject a held write
Resolve a write held at an approval gate. On approve Patchwork performs the write server-side and resumes the run; on reject the model is told it was declined and the run resumes so the agent can respond. Find the tool_call_id in pending_confirmation on GET /v1/loom/threads, or in the write.confirmation_requested realtime event.
curl --request POST \
--url https://api.usepatchwork.co/v1/loom/runs/{run_id}/confirm \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"tool_call_id": "9b1deb4d-3b7d-4bad-9bdd-2b0d7b3dcb6d",
"decision": "approve"
}
'const options = {
method: 'POST',
headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'},
body: JSON.stringify({tool_call_id: '9b1deb4d-3b7d-4bad-9bdd-2b0d7b3dcb6d', decision: 'approve'})
};
fetch('https://api.usepatchwork.co/v1/loom/runs/{run_id}/confirm', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));import requests
url = "https://api.usepatchwork.co/v1/loom/runs/{run_id}/confirm"
payload = {
"tool_call_id": "9b1deb4d-3b7d-4bad-9bdd-2b0d7b3dcb6d",
"decision": "approve"
}
headers = {
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text){
"status": "success",
"data": {
"run_id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"state": "running"
}
}{
"status": "error",
"error": {
"message": "<string>",
"code": "<string>",
"details": "<unknown>"
}
}{
"status": "error",
"error": {
"message": "<string>",
"code": "<string>",
"details": "<unknown>"
}
}{
"status": "error",
"error": {
"message": "<string>",
"code": "<string>",
"details": "<unknown>"
}
}Authorizations
Identify accepts a workspace API key (sk_…). Loom accepts either a short-lived session token (direct) or a workspace API key (relay).
Headers
Makes this POST safe to retry. The first call does the work and its response is stored; a retry with the same key returns that stored response with Idempotency-Replayed: true, without charging again. Same key with a different body is rejected. Keys expire after 24 hours.
255Relay only. Opaque subject id, chosen by you and never resolved by Patchwork. Partitions threads and memory within your workspace. Direct tokens carry sub instead — do not send this header on a session token.
Required on every subject-scoped resource: threads, messages, runs, outcomes, confirmations and realtime. Omitting it is subject_required, never a wider result. The value is used exactly as given — never trimmed or normalised — so a stray space creates a second partition and is refused as invalid_subject.
Connection id or name the agent may call. Required in relay when the agent has unpinned customer tools. Direct tokens may carry conn instead.