Cowdidocs

Get a user’s status

GET
/v1/internal/kyc/status

Internal KYC status endpoint for backend, backoffice, and onboarding orchestration. The /v1/internal/kyc form is exposed on the shared API host; the /api/internal/kyc form remains available for cluster-local service calls. Authenticated with either a backend user bearer token or a WorkOS M2M token holding org:kyc.applications:read; returns the backend-compatible status DTO for a user. A machine caller must pass a non-empty organization_id naming the tenant to read; a user token may not pass it, since its organization comes from the token.

AuthorizationBearer <token>

Create a client with POST /v1/oauth/clients, then exchange its credentials for an access token. AUTHORIZATION_CODE clients act on behalf of a signed-in user and always use PKCE; CLIENT_CREDENTIALS clients act as themselves. A client may only be granted scopes its creator already holds — read the grantable list from GET /v1/oauth/clients/scopes.

In: header

Query Parameters

user_id*string

Global user id (JWT sub) to look up.

Formatuuid
organization_id?string

Target organization to scope the lookup to. Honored only for a platform (cowdi) API key holding PLATFORM-scoped access; every other caller resolves the organization from its bearer token and must omit this parameter (otherwise 400/403).

Length1 <= length

Response Body

application/json

application/json

application/json

application/json

application/json

application/json

application/json

curl -X GET "https://example.com/v1/internal/kyc/status?user_id=550e8400-e29b-41d4-a716-446655440000"
{  "user_id": "a169451c-8525-4352-b8ca-070dd449a1a5",  "organization_id": "string",  "application_id": "48ac72d0-a829-4896-a067-dcb1c2b0f30c",  "status": "DRAFT",  "user_type": "INDIVIDUAL",  "merchant_type": "SOLE_PROPRIETOR",  "verification_result": "PENDING",  "verification_reason_code": "string",  "verification_reason": "string",  "external_reason_code": "DOCUMENT_IMAGE_UNUSABLE",  "resubmission_count": 0,  "reapply_blocked_until": "2026-06-04T10:20:00.000Z",  "verification_completed_at": "2026-06-04T10:20:00.000Z",  "submitted_at": "2026-06-04T10:20:00.000Z",  "updated_at": "2026-06-04T10:20:00.000Z",  "subject": {    "type": "merchant",    "id": "string",    "phone": "string",    "organization_user_id": "string",    "name": "string",    "email": "string",    "id_number": "string",    "date_of_birth": "1990-05-21",    "gender": "MALE",    "identity_type": "NATIONAL_ID",    "identity_country": "GB"  }}
{  "code": "INVALID_PARAMS",  "description": "user_id must be a UUID",  "identifier": "string",  "invalid_params": [    {      "path": "user_id",      "reason": "must be a UUID",      "sub_code": "string"    }  ]}
{  "code": "INVALID_PARAMS",  "description": "user_id must be a UUID",  "identifier": "string",  "invalid_params": [    {      "path": "user_id",      "reason": "must be a UUID",      "sub_code": "string"    }  ]}
{  "code": "INVALID_PARAMS",  "description": "user_id must be a UUID",  "identifier": "string",  "invalid_params": [    {      "path": "user_id",      "reason": "must be a UUID",      "sub_code": "string"    }  ]}
{  "code": "INVALID_PARAMS",  "description": "user_id must be a UUID",  "identifier": "string",  "invalid_params": [    {      "path": "user_id",      "reason": "must be a UUID",      "sub_code": "string"    }  ]}
{  "code": "INVALID_PARAMS",  "description": "user_id must be a UUID",  "identifier": "string",  "invalid_params": [    {      "path": "user_id",      "reason": "must be a UUID",      "sub_code": "string"    }  ]}
{  "code": "INVALID_PARAMS",  "description": "user_id must be a UUID",  "identifier": "string",  "invalid_params": [    {      "path": "user_id",      "reason": "must be a UUID",      "sub_code": "string"    }  ]}

Start the expiration sweep POST

Runs the stale-session expiry sweep for the caller’s organization. The service also runs this job on its own schedule across every organization; this endpoint is the operator-triggered form, narrowed to one tenant. The sweep reconciles applications that have sat in ID_VERIFICATION_PENDING past the provider submission window: a job the provider reports as final is completed, and one the provider no longer knows about is moved to EXPIRED — nothing is expired without asking the provider first. Authenticated with an internal user’s org-scoped bearer token and authorized by a WorkOS check for org:kyc.override:write on the caller’s own organization. The organization is resolved from the token; passing organization_id is rejected. The run is held to the same interval as the scheduled one — there is no force bypass — and each organization has its own lease and due-window, so a manual run neither blocks nor is blocked by the global schedule.

Get a user’s subject details GET

Internal endpoint for backend services. The /v1/internal/kyc form is available on the shared API host; the /api/internal/kyc form remains available for cluster-local service calls. Authenticated with either a backend user bearer token or a WorkOS M2M token holding org:kyc.applications:read; returns the full identity and document payload for a KYC subject (ID and selfie images inlined as base64), used to submit a Choice Bank onboarding during current-account self-activation. A machine caller must pass a non-empty organization_id naming the tenant to read; a user token may not pass it. Returns 404 when the user has no provisioned subject yet.