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

text
GET https://signedapproval.net/api/v1/approvals/:id
Authorization: Bearer sa_live_...

Response (Pending)

JSON
{
  "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)

JSON
{
  "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)

JSON
{
  "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)

JSON
{
  "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:

javascript
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.