§ API · Authentication
Authenticate integrator API requests
Integrator endpoints are mounted under /api/nuvouch. Use x-api-key as the canonical server authentication header.
headers
requiredx-api-key: $NUVOUCH_INTEGRATOR_API_KEY
content-type: application/json
# also accepted by the backend:
# Authorization: ApiKey $NUVOUCH_INTEGRATOR_API_KEY§ API · Linking
Create or reuse a connection session for a business subject
POST/api/nuvouch/v1/integrator/connections/sessions
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/api/nuvouch/v1/integrator/connections/sessions" \
-H "x-api-key: $NUVOUCH_INTEGRATOR_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{
"sessionId": "conn_sess_123",
"status": "pending",
"expiresAt": "2026-04-21T18:00:00.000Z",
"qrUrl": "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/api/nuvouch/v1/integrator/connections/sessions/:id
curl
check sessioncurl "https://api.nuvouch.com/api/nuvouch/v1/integrator/connections/sessions/conn_sess_123" \
-H "x-api-key: $NUVOUCH_INTEGRATOR_API_KEY"response
session envelope{
"session": {
"id": "conn_sess_123",
"sessionId": "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/api/nuvouch/v1/integrator/connections/lookup?subjectId=:subjectId&contextKey=:contextKey
curl
lookup active linkcurl "https://api.nuvouch.com/api/nuvouch/v1/integrator/connections/lookup?subjectId=cus_123&contextKey=merchant:acct_live_001" \
-H "x-api-key: $NUVOUCH_INTEGRATOR_API_KEY"GET/api/nuvouch/v1/integrator/connections?subjectId=:subjectId&status=:status
curl
list connectionscurl "https://api.nuvouch.com/api/nuvouch/v1/integrator/connections?subjectId=cus_123&status=active" \
-H "x-api-key: $NUVOUCH_INTEGRATOR_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/api/nuvouch/v1/integrator/connections/:id/revoke
curl
revoke connectioncurl -X POST "https://api.nuvouch.com/api/nuvouch/v1/integrator/connections/conn_123/revoke" \
-H "x-api-key: $NUVOUCH_INTEGRATOR_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/api/nuvouch/v1/integrator/approval-requests
Request body
| Field | Type | Description |
|---|---|---|
| targetUserId | targetConnectionId | targetSubject | one required | Exactly one targeting mode. New integrations should prefer targetSubject. |
| externalRequestIdrequired | string | Your durable business id such as payment_auth_001. |
| titlerequired | string | Headline displayed to approver. |
| summaryrequired | string | Short context shown in request details. |
| requestedForrequired | string | Business domain label, such as Payment authorization. |
| actor.id / actor.namerequired | string | Who initiated the approval request in your system. |
| context.kind / context.title / context.reasonrequired | object | What is being approved and why. |
| context.expiresAtrequired | ISO datetime | Decision expiry timestamp. |
| context.referenceCoderequired | string | Your join key such as a payment intent id. |
| risk.levelrequired | low | medium | high | Risk emphasis and policy gating. |
| actions | array | Optional explicit action labels shown to the user. |
| amount | string | Optional human-readable amount such as $84.00. |
curl
create approvalcurl -X POST "https://api.nuvouch.com/api/nuvouch/v1/integrator/approval-requests" \
-H "x-api-key: $NUVOUCH_INTEGRATOR_API_KEY" \
-H "content-type: application/json" \
-d '{
"targetSubject": {
"subjectId": "cus_123",
"contextKey": "merchant:acct_live_001"
},
"externalRequestId": "payment_auth_001",
"title": "Approve payment",
"summary": "Stripe needs the customer to confirm a high-risk payment.",
"requestedFor": "Payment authorization",
"actor": {
"id": "payments_agent_01",
"name": "Stripe Payment Agent",
"subtitle": "Payments risk workflow",
"avatarLabel": "ST"
},
"context": {
"kind": "digital-service",
"title": "Card payment",
"location": "Stripe",
"reason": "High-risk payment requires customer confirmation",
"expiresAt": "2026-04-21T18:00:00.000Z",
"referenceCode": "pi_123"
},
"risk": {
"level": "high",
"summary": "Customer confirmation required before capture",
"checks": ["3DS fallback unavailable", "Risk score above threshold"]
},
"actions": [
{ "label": "Approve payment", "value": "approve" },
{ "label": "Deny payment", "value": "deny" }
],
"amount": "$84.00"
}'response
201 created{
"approvalRequest": {
"id": "req_2f5cd6dbb6784b9ab2f7",
"externalRequestId": "payment_auth_001",
"targetUserId": "usr_nuvouch_456",
"status": "pending",
"title": "Approve payment",
"summary": "Stripe needs the customer to confirm a high-risk payment.",
"requestedFor": "Payment authorization",
"amount": "$84.00",
"createdAt": "2026-04-21T16:00:00.000Z",
"decisionMethod": null,
"decisionNote": null,
"decisionDecidedAt": null,
"cancelledAt": null,
"context": {
"kind": "digital-service",
"title": "Card payment",
"location": "Stripe",
"reason": "High-risk payment requires customer confirmation",
"expiresAt": "2026-04-21T18:00:00.000Z",
"referenceCode": "pi_123"
},
"risk": {
"level": "high",
"summary": "Customer confirmation required before capture",
"checks": ["3DS fallback unavailable", "Risk score above threshold"]
},
"actions": [
{ "label": "Approve payment", "value": "approve" },
{ "label": "Deny payment", "value": "deny" }
]
}
}§ 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/api/nuvouch/v1/integrator/approval-requests/:id
curl
by idcurl "https://api.nuvouch.com/api/nuvouch/v1/integrator/approval-requests/req_2f5cd6dbb6784b9ab2f7" \
-H "x-api-key: $NUVOUCH_INTEGRATOR_API_KEY"GET/api/nuvouch/v1/integrator/approval-requests?external_id=:externalId
curl
by external idcurl "https://api.nuvouch.com/api/nuvouch/v1/integrator/approval-requests?external_id=payment_auth_001" \
-H "x-api-key: $NUVOUCH_INTEGRATOR_API_KEY"response
approved example{
"approvalRequest": {
"id": "req_2f5cd6dbb6784b9ab2f7",
"externalRequestId": "payment_auth_001",
"status": "approved",
"title": "Approve payment",
"amount": "$84.00",
"decisionMethod": "biometric",
"decisionNote": "Approved from trusted device",
"decisionDecidedAt": "2026-04-21T16:14:11.000Z"
}
}§ API · Lifecycle
Cancel pending request
POST/api/nuvouch/v1/integrator/approval-requests/:id/cancel
curl
cancelcurl -X POST "https://api.nuvouch.com/api/nuvouch/v1/integrator/approval-requests/req_2f5cd6dbb6784b9ab2f7/cancel" \
-H "x-api-key: $NUVOUCH_INTEGRATOR_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 targetSubject or targetConnectionId. |
| 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."
}
}