§ API · Authentication

Authenticate integrator API requests

Integrator endpoints are mounted under /api. Use x-api-key as the canonical server authentication header.

headers
required
x-api-key: $NUVOUCH_API_KEY
content-type: application/json

# also accepted by the backend:
# Authorization: Bearer $NUVOUCH_API_KEY
§ API · Connection lifecycle

Inspect, list, and revoke active links

GET/v1/links/:id
curl
check session
curl "https://api.nuvouch.com/v1/links/conn_sess_123" \
  -H "x-api-key: $NUVOUCH_API_KEY"
response
session envelope
{
  "session": {
    "id": "conn_sess_123",
    "linkId": "conn_sess_123",
    "status": "accepted",
    "expiresAt": "2026-04-21T18:00:00.000Z",
    "acceptedAt": "2026-04-21T16:05:00.000Z",
    "subject": {
      "id": "cus_123",
      "label": "Ada Lovelace"
    },
    "context": {
      "key": "merchant:acct_live_001",
      "type": "merchant",
      "label": "Stripe Live Account"
    },
    "connection": {
      "id": "conn_123",
      "nuvouchUserId": "usr_nuvouch_456",
      "subject": {
        "id": "cus_123",
        "label": "Ada Lovelace"
      },
      "context": {
        "key": "merchant:acct_live_001",
        "type": "merchant",
        "label": "Stripe Live Account"
      },
      "capability": "hitl_only",
      "status": "active"
    }
  }
}
GET/v1/connections/lookup?subjectId=:subjectId&contextKey=:contextKey
curl
lookup active link
curl "https://api.nuvouch.com/v1/connections/lookup?subjectId=cus_123&contextKey=merchant:acct_live_001" \
  -H "x-api-key: $NUVOUCH_API_KEY"
GET/v1/connections?subjectId=:subjectId&status=:status
curl
list connections
curl "https://api.nuvouch.com/v1/connections?subjectId=cus_123&status=active" \
  -H "x-api-key: $NUVOUCH_API_KEY"
response
list envelope
{
  "items": [
    {
      "id": "conn_123",
      "nuvouchUserId": "usr_nuvouch_456",
      "subject": {
        "id": "cus_123",
        "label": "Ada Lovelace"
      },
      "context": {
        "key": "merchant:acct_live_001",
        "type": "merchant",
        "label": "Stripe Live Account"
      },
      "capability": "hitl_only",
      "status": "active",
      "lastConfirmedAt": "2026-04-21T16:05:00.000Z",
      "revokedAt": null,
      "createdAt": "2026-04-21T16:05:00.000Z",
      "updatedAt": "2026-04-21T16:05:00.000Z"
    }
  ]
}
POST/v1/connections/:id/revoke
curl
revoke connection
curl -X POST "https://api.nuvouch.com/v1/connections/conn_123/revoke" \
  -H "x-api-key: $NUVOUCH_API_KEY"
response
revoked connection
{
  "connection": {
    "id": "conn_123",
    "nuvouchUserId": "usr_nuvouch_456",
    "subject": {
      "id": "cus_123",
      "label": "Ada Lovelace"
    },
    "context": {
      "key": "merchant:acct_live_001",
      "type": "merchant",
      "label": "Stripe Live Account"
    },
    "capability": "hitl_only",
    "status": "revoked",
    "lastConfirmedAt": "2026-04-21T16:05:00.000Z",
    "revokedAt": "2026-04-21T17:00:00.000Z",
    "createdAt": "2026-04-21T16:05:00.000Z",
    "updatedAt": "2026-04-21T17:00:00.000Z"
  }
}
§ API · Approvals

Create approval request (payment authorization flow)

POST/v1/approvals

Request body

FieldTypeDescription
subject.idrequiredstringIdentifies the user who will receive the request.
source.keyrequiredstringStable origin key such as stripe:acct_123.
source.namestringOptional display name for the source.
action.typerequiredstringMachine-readable action identifier such as payment.authorization.
action.titlerequiredstringShort heading shown to the user.
action.descriptionrequiredstringExplains what will happen and why it matters.
amount.value / amount.currencyobjectOptional financial amount shown prominently in UI.
details[]arrayOptional ordered list of short key/value facts.
review.items[]arrayOptional long text and safe links shown before decision.
postDecisionobjectOptional user-tapped next steps shown after a terminal decision.
actor.type / actor.nameobjectWho or what triggered the request.
risk.level / risk.signalsobjectRisk summary and reasons the user should review.
decisions[]requiredarrayChoices shown to the user.
metadataobjectInternal data returned in callbacks, not shown to users.
externalRequestIdstringOptional durable business id for retry dedupe.
curl
create approval
curl -X POST "https://api.nuvouch.com/v1/approvals" \
  -H "x-api-key: $NUVOUCH_API_KEY" \
  -H "content-type: application/json" \
  -d '{
    "subject": {
      "id": "user_123"
    },
    "source": {
      "key": "stripe:acct_123",
      "name": "Stripe"
    },
    "action": {
      "type": "payment.authorization",
      "title": "Approve payment",
      "description": "Allow Billing Agent to charge $84.00"
    },
    "amount": {
      "value": 84.0,
      "currency": "USD"
    },
    "details": [
      { "label": "Merchant", "value": "OpenAI API" },
      { "label": "Billing period", "value": "April 2026" }
    ],
    "review": {
      "items": [
        {
          "type": "text",
          "title": "Agent note",
          "body": "Billing Agent found usage above the configured threshold and needs approval before charging the card."
        },
        {
          "type": "link",
          "label": "Review checkout",
          "url": "https://checkout.example.com/session/payment_auth_001",
          "description": "Open the payment page before approving.",
          "purpose": "payment",
          "requiredBeforeApproval": true
        }
      ]
    },
    "actor": {
      "type": "ai_agent",
      "name": "Billing Agent"
    },
    "risk": {
      "level": "medium",
      "signals": ["automated_action", "usage_threshold_exceeded"]
    },
    "decisions": [
      { "label": "Approve", "value": "approve" },
      { "label": "Deny", "value": "deny" }
    ],
    "metadata": {
      "orderId": "ord_123"
    },
    "postDecision": {
      "approved": {
        "message": "Payment approved. Continue to checkout if confirmation is required.",
        "actions": [
          {
            "type": "open_url",
            "label": "Continue checkout",
            "url": "https://checkout.example.com/session/payment_auth_001"
          }
        ]
      }
    },
    "externalRequestId": "payment_auth_001"
  }'
response
201 created
{
  "id": "req_2f5cd6dbb6784b9ab2f7",
  "externalRequestId": "payment_auth_001",
  "subject": {
    "id": "user_123"
  },
  "source": {
    "key": "stripe:acct_123",
    "name": "Stripe"
  },
  "status": "pending",
  "action": {
    "type": "payment.authorization",
    "title": "Approve payment",
    "description": "Allow Billing Agent to charge $84.00"
  },
  "amount": {
    "value": 84,
    "currency": "USD"
  },
  "createdAt": "2026-04-21T16:00:00.000Z",
  "details": [
    { "label": "Merchant", "value": "OpenAI API" },
    { "label": "Billing period", "value": "April 2026" }
  ],
  "review": {
    "items": [
      {
        "type": "text",
        "title": "Agent note",
        "body": "Billing Agent found usage above the configured threshold and needs approval before charging the card."
      },
      {
        "type": "link",
        "label": "Review checkout",
        "url": "https://checkout.example.com/session/payment_auth_001",
        "description": "Open the payment page before approving.",
        "purpose": "payment",
        "requiredBeforeApproval": true
      }
    ]
  },
  "actor": {
    "type": "ai_agent",
    "name": "Billing Agent"
  },
  "risk": {
    "level": "medium",
    "signals": ["automated_action", "usage_threshold_exceeded"]
  },
  "decisions": [
    { "label": "Approve", "value": "approve" },
    { "label": "Deny", "value": "deny" }
  ],
  "metadata": {
    "orderId": "ord_123"
  },
  "postDecision": {
    "approved": {
      "message": "Payment approved. Continue to checkout if confirmation is required.",
      "actions": [
        {
          "type": "open_url",
          "label": "Continue checkout",
          "url": "https://checkout.example.com/session/payment_auth_001"
        }
      ]
    }
  }
}
§ API · Idempotency

Retry approval creation without creating duplicates

Approval creation deduplicates by (integrator, externalRequestId). Treat externalRequestId as the durable identity for the approval request you are trying to create.

  • Reuse the same externalRequestId on network retries, worker recovery, or queue replays.
  • If create returns DUPLICATE_EXTERNAL_ID, fetch by external_id instead of creating a new request.
  • Do not mint a fresh external id just because the first HTTP attempt had an uncertain outcome.
error-body.json
duplicate external id
{
  "error": {
    "code": "DUPLICATE_EXTERNAL_ID",
    "message": "Duplicate external request id payment_auth_001"
  }
}
§ API · Reads

Read approval request state for support and reconciliation

GET/v1/approvals/:id
curl
by id
curl "https://api.nuvouch.com/v1/approvals/req_2f5cd6dbb6784b9ab2f7" \
  -H "x-api-key: $NUVOUCH_API_KEY"
GET/v1/approvals?external_id=:externalId
curl
by external id
curl "https://api.nuvouch.com/v1/approvals?external_id=payment_auth_001" \
  -H "x-api-key: $NUVOUCH_API_KEY"
response
approved example
{
  "id": "req_2f5cd6dbb6784b9ab2f7",
  "externalRequestId": "payment_auth_001",
  "status": "approved",
  "subject": { "id": "user_123" },
  "source": { "key": "stripe:acct_123", "name": "Stripe" },
  "action": {
    "type": "payment.authorization",
    "title": "Approve payment",
    "description": "Allow Billing Agent to charge $84.00"
  },
  "amount": { "value": 84, "currency": "USD" },
  "decisionMethod": "biometric",
  "decisionNote": "Approved from trusted device",
  "decisionDecidedAt": "2026-04-21T16:14:11.000Z",
  "createdAt": "2026-04-21T16:00:00.000Z",
  "expiresAt": "2026-04-21T18:00:00.000Z"
}
§ API · Lifecycle

Cancel pending request

POST/v1/approvals/:id/cancel
curl
cancel
curl -X POST "https://api.nuvouch.com/v1/approvals/req_2f5cd6dbb6784b9ab2f7/cancel" \
  -H "x-api-key: $NUVOUCH_API_KEY"

Cancel works only while request is pending. Terminal states (approved, denied, expired, cancelled) return an error.

§ API · Errors

Stable error codes

FieldTypeDescription
API_KEY_REQUIRED401Integrator authentication header was missing.
INTEGRATOR_KEY_UNBOUND403API key exists but is not bound to an integrator.
INTEGRATOR_INACTIVE403Integrator is inactive or not provisioned.
INTEGRATOR_CALLBACK_NOT_CONFIGURED409Callback URL must be configured before creating connection sessions.
REQUEST_NOT_FOUND404Unknown approval request id or external id.
REQUEST_ALREADY_TERMINAL409Cannot cancel or mutate a completed request.
DUPLICATE_EXTERNAL_ID409This integrator has already used that externalRequestId.
CONNECTION_ALREADY_LINKED409The requested subject/context tuple already has an active link.
CONNECTION_NOT_FOUND404Unknown connection id or no active lookup match for the subject tuple.
CONNECTION_SESSION_NOT_FOUND404Unknown connection session id.
CONNECTION_SESSION_EXPIRED409Connection session or challenge expired before acceptance.
CONNECTION_CONFLICT409Session or challenge state conflicts with the attempted action.
UNLINKED_TARGET409No active connection exists for the supplied subject and source.
RATE_LIMIT_EXCEEDED429Endpoint-specific rate limit hit. Respect Retry-After and retry later.
VALIDATION_FAILED400Input body or query shape failed validation.
FORBIDDEN403Caller is authenticated but does not have permission for that resource.
UNKNOWN_USER404targetUserId refers to a Nuvouch user that does not exist.
error-body.json
{
  "error": {
    "code": "RATE_LIMIT_EXCEEDED",
    "message": "Too many approval status reads. Retry after 42 seconds."
  }
}