§ API · Authentication
Authenticate integrator API requests
Integrator endpoints are mounted under /api. Use x-api-key as the canonical server authentication header.
headers
requiredx-api-key: $NUVOUCH_API_KEY
content-type: application/json
# also accepted by the backend:
# Authorization: Bearer $NUVOUCH_API_KEY§ API · Linking
Create or reuse a connection session for a business subject
POST/v1/links
Request body
| Field | Type | Description |
|---|---|---|
| subjectIdrequired | string | Your customer or employee id, such as cus_123. |
| subjectLabelrequired | string | Human label displayed during linking. |
| contextKey | string | Optional scope such as merchant:acct_live_001. |
| contextType | string | Optional type when contextKey is present, such as merchant. |
| contextLabel | string | Optional human label for the scoped context. |
curl
create connection sessioncurl -X POST "https://api.nuvouch.com/v1/links" \
-H "x-api-key: $NUVOUCH_API_KEY" \
-H "content-type: application/json" \
-d '{
"subjectId": "cus_123",
"subjectLabel": "Ada Lovelace",
"contextKey": "merchant:acct_live_001",
"contextType": "merchant",
"contextLabel": "Stripe Live Account"
}'response
201 created{
"linkId": "conn_sess_123",
"status": "pending",
"expiresAt": "2026-04-21T18:00:00.000Z",
"url": "https://connect.nuvouch.app/connect?t=opaque_token",
"shortCode": "ABCD1234"
}response
409 already linked{
"error": {
"code": "CONNECTION_ALREADY_LINKED",
"message": "This subject is already linked to a Nuvouch user"
},
"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"
}
}§ API · Connection lifecycle
Inspect, list, and revoke active links
GET/v1/links/:id
curl
check sessioncurl "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 linkcurl "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 connectionscurl "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 connectioncurl -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
| Field | Type | Description |
|---|---|---|
| subject.idrequired | string | Identifies the user who will receive the request. |
| source.keyrequired | string | Stable origin key such as stripe:acct_123. |
| source.name | string | Optional display name for the source. |
| action.typerequired | string | Machine-readable action identifier such as payment.authorization. |
| action.titlerequired | string | Short heading shown to the user. |
| action.descriptionrequired | string | Explains what will happen and why it matters. |
| amount.value / amount.currency | object | Optional financial amount shown prominently in UI. |
| details[] | array | Optional ordered list of short key/value facts. |
| review.items[] | array | Optional long text and safe links shown before decision. |
| postDecision | object | Optional user-tapped next steps shown after a terminal decision. |
| actor.type / actor.name | object | Who or what triggered the request. |
| risk.level / risk.signals | object | Risk summary and reasons the user should review. |
| decisions[]required | array | Choices shown to the user. |
| metadata | object | Internal data returned in callbacks, not shown to users. |
| externalRequestId | string | Optional durable business id for retry dedupe. |
curl
create approvalcurl -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
externalRequestIdon network retries, worker recovery, or queue replays. - If create returns
DUPLICATE_EXTERNAL_ID, fetch byexternal_idinstead 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 idcurl "https://api.nuvouch.com/v1/approvals/req_2f5cd6dbb6784b9ab2f7" \
-H "x-api-key: $NUVOUCH_API_KEY"GET/v1/approvals?external_id=:externalId
curl
by external idcurl "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
cancelcurl -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
| Field | Type | Description |
|---|---|---|
| API_KEY_REQUIRED | 401 | Integrator authentication header was missing. |
| INTEGRATOR_KEY_UNBOUND | 403 | API key exists but is not bound to an integrator. |
| INTEGRATOR_INACTIVE | 403 | Integrator is inactive or not provisioned. |
| INTEGRATOR_CALLBACK_NOT_CONFIGURED | 409 | Callback URL must be configured before creating connection sessions. |
| REQUEST_NOT_FOUND | 404 | Unknown approval request id or external id. |
| REQUEST_ALREADY_TERMINAL | 409 | Cannot cancel or mutate a completed request. |
| DUPLICATE_EXTERNAL_ID | 409 | This integrator has already used that externalRequestId. |
| CONNECTION_ALREADY_LINKED | 409 | The requested subject/context tuple already has an active link. |
| CONNECTION_NOT_FOUND | 404 | Unknown connection id or no active lookup match for the subject tuple. |
| CONNECTION_SESSION_NOT_FOUND | 404 | Unknown connection session id. |
| CONNECTION_SESSION_EXPIRED | 409 | Connection session or challenge expired before acceptance. |
| CONNECTION_CONFLICT | 409 | Session or challenge state conflicts with the attempted action. |
| UNLINKED_TARGET | 409 | No active connection exists for the supplied subject and source. |
| RATE_LIMIT_EXCEEDED | 429 | Endpoint-specific rate limit hit. Respect Retry-After and retry later. |
| VALIDATION_FAILED | 400 | Input body or query shape failed validation. |
| FORBIDDEN | 403 | Caller is authenticated but does not have permission for that resource. |
| UNKNOWN_USER | 404 | targetUserId 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."
}
}