Your First Approval
End-to-end walkthrough: create an API key, send an approval request, approve it on your phone, and verify the signed decision.
Create an API key
Go to signedapproval.net/dashboard/api-keys (or navigate to Integrations → API Keys from the dashboard). Click Create Keyand give it a name like "Test Key".
Your API key will be displayed once — copy it and store it securely. Keys use the format sa_live_....
Send an approval request
Use curl (or any HTTP client) to create an approval request:
curl -X POST https://signedapproval.net/api/v1/approvals/request \
-H "Authorization: Bearer sa_live_your_key_here" \
-H "Content-Type: application/json" \
-d '{
"action": "Deploy v2.1.0 to production",
"ttl_seconds": 3600,
"metadata": {
"environment": "production",
"version": "2.1.0"
}
}'You'll get back a response with the request ID and status:
{
"id": "req_abc123...",
"status": "pending",
"action": "Deploy v2.1.0 to production",
"created_at": "2026-03-23T14:00:00.000Z",
"expires_at": "2026-03-23T15:00:00.000Z"
}Approve the request
The request appears in two places:
- Web dashboard — Log in at signedapproval.net/dashboard to see pending requests.
- iOS app — If you've installed the app and enabled push notifications, you'll receive an alert.
Tap or click the request to review the action details, then authenticate with your registered method (passkey, TOTP, or Face ID) and choose Approve or Reject.
Check the status
Poll for the decision status:
curl https://signedapproval.net/api/v1/approvals/req_abc123 \
-H "Authorization: Bearer sa_live_your_key_here"Once approved, the response includes the signed decision:
{
"id": "req_abc123...",
"status": "approved",
"decision": {
"id": "dec_xyz789...",
"decision": "approved",
"method": "passkey",
"signed_payload": "eyJ2IjoxLCJyaWQiOi...",
"signature": "base64-ed25519-signature...",
"decided_at": "2026-03-23T14:02:30.000Z"
}
}Verify the signature
Anyone can verify the signed decision — no authentication required:
curl https://signedapproval.net/api/v1/approvals/req_abc123/verifyThe response confirms whether the signature is valid:
{
"valid": true,
"decision": "approved",
"method": "passkey",
"verified_at": "2026-03-23T14:03:00.000Z",
"public_key": "base64-ed25519-public-key...",
"signature": "base64-ed25519-signature...",
"payload": { ... }
}What Happens Under the Hood
When you approve a request, SignedApproval:
- Verifies your authentication (passkey challenge, TOTP code, or biometric).
- Constructs a canonical JSON payload with the request ID, decision, method, timestamp, expiry, and a random nonce.
- Signs the payload with your Ed25519 private key (decrypted from AES-256-GCM encrypted storage).
- Stores the signed decision in the database.
- Fires a webhook to the caller (if configured) with the signed payload.
The signature is mathematically bound to the exact payload. Any tampering — changing the decision, timestamp, or any other field — makes the signature invalid.