Check Approval Status
GET /api/v1/approvals/:id -- poll for the current status of an approval request and retrieve the signed decision.
Key Concepts
After creating an approval request, use this endpoint to check its status. The request transitions through states: pending (waiting for decision), approved (human approved),rejected (human rejected), or expired (TTL elapsed).
When the status is "approved" or "rejected", the response includes the full signed decision with the Ed25519 signature that can be independently verified.
Endpoint
GET https://signedapproval.net/api/v1/approvals/:id
Authorization: Bearer sa_live_...Response (Pending)
{
"id": "req_abc123def456",
"status": "pending",
"action": "Deploy v2.1.0 to production",
"metadata": { "environment": "production" },
"created_at": "2026-03-23T14:00:00.000Z",
"expires_at": "2026-03-23T15:00:00.000Z"
}Response (Approved)
{
"id": "req_abc123def456",
"status": "approved",
"action": "Deploy v2.1.0 to production",
"metadata": { "environment": "production" },
"created_at": "2026-03-23T14:00:00.000Z",
"expires_at": "2026-03-23T15:00:00.000Z",
"decision": {
"id": "dec_xyz789abc012",
"decision": "approved",
"method": "passkey",
"signed_payload": "eyJ2IjoxLCJyaWQiOiJyZXFfYWJjMTIz...",
"signature": "Wm9tSW1hZ2luYXJ5U2lnbmF0dXJl...",
"decided_at": "2026-03-23T14:02:30.000Z"
}
}Response (Rejected)
{
"id": "req_abc123def456",
"status": "rejected",
"action": "Deploy v2.1.0 to production",
"decision": {
"id": "dec_xyz789abc012",
"decision": "rejected",
"method": "totp",
"signed_payload": "eyJ2IjoxLCJyaWQiOiJyZXFfYWJjMTIz...",
"signature": "UmVqZWN0aW9uU2lnbmF0dXJl...",
"decided_at": "2026-03-23T14:05:00.000Z"
}
}Response (Expired)
{
"id": "req_abc123def456",
"status": "expired",
"action": "Deploy v2.1.0 to production",
"created_at": "2026-03-23T14:00:00.000Z",
"expires_at": "2026-03-23T15:00:00.000Z"
}Expired requests have no decision field -- no signed artifact is produced for expirations.
Polling Pattern
If you are not using webhooks, poll this endpoint to wait for a decision:
async function waitForApproval(requestId: string, apiKey: string) {
const maxWait = 300_000; // 5 minutes
const interval = 3_000; // 3 seconds
const start = Date.now();
while (Date.now() - start < maxWait) {
const res = await fetch(
`https://signedapproval.net/api/v1/approvals/${requestId}`,
{ headers: { Authorization: `Bearer ${apiKey}` } }
);
const data = await res.json();
if (data.status !== 'pending') {
return data; // approved, rejected, or expired
}
await new Promise(r => setTimeout(r, interval));
}
throw new Error('Timed out waiting for approval');
}Note
The status endpoint requires the same API key that created the request (or an internal service token). The public
/verify endpoint is available without authentication for cryptographic verification.Tip
For production systems, prefer webhooks over polling. Webhooks deliver decisions instantly and reduce the load on both your application and SignedApproval's API.
Related Articles