CorebanqCorebanq Developer Docs
Usersv1Registration

Resend OTP

AUTHENTICATED BY X-API-Key, not by a bearer token. auth.APIKeyMiddleware REPLACES the usual chain on this route, so there is no user context, no RBAC check and no licence check — and the refusals are common.api_key_required / common.invalid_api_key rather than common.unauthorized / common.rbac_no_rec_access. An OPTIONS request skips the key check entirely. Ask for the registration code again. One answer covers every outcome: a credential still waiting for its code is sent another, and a request that names an address nobody registered, one already confirmed, or an account with no credential is answered identically without sending anything. Send pacing applies to both, so a burst answers `429`. The endpoint is also rate limited per IP.

POST
/v1/users/resend-otp

Authorization

apiKeyAuth
X-API-Key<token>

Service API key for the eight pre-authentication user routes. auth.APIKeyMiddleware REPLACES the bearer chain on those routes rather than wrapping it: there is no user context, no RBAC check and no licence check on them.

In: header

Request Body

application/json

TypeScript Definitions

Use the request body type in TypeScript.

Identify the account by user_id or by credential_value; one of the two is required. cred_id narrows the reissue to one credential of that account and is not an identifier on its own — a body carrying it alone resolves no account. POST /v1/users/initiate-registration no longer answers with an identifier, so a client driving registration end to end carries the address it submitted and sends that. A body naming neither is refused with 400 users_m.invalid_user_id: it names no address, so refusing it discloses nothing.

Identify the account by user_id or by credential_value; one of the two is required. cred_id narrows the reissue to one credential of that account and is not an identifier on its own — a body carrying it alone resolves no account. POST /v1/users/initiate-registration no longer answers with an identifier, so a client driving registration end to end carries the address it submitted and sends that. A body naming neither is refused with 400 users_m.invalid_user_id: it names no address, so refusing it discloses nothing.

Response Body

application/json

application/json

application/json

application/json

application/json

application/json

application/json

curl -X POST "https://example.com/v1/users/resend-otp" \  -H "Content-Type: application/json" \  -d '{}'
{
  "status": 200,
  "message": "We've sent a verification code to ada@example.com. Please check and enter the code.",
  "retry_after": 30
}
{
  "status": 400,
  "code": "users_m.invalid_user_id",
  "message": "Invalid user ID",
  "class": "validation"
}
{
  "status": 401,
  "message": "common.api_key_required",
  "code": "common.api_key_required",
  "class": "business"
}
{
  "status": 403,
  "message": "common.invalid_api_key",
  "code": "common.invalid_api_key",
  "class": "business"
}

{
  "status": 429,
  "code": "users_m.resend_rate_limit_exceeded",
  "message": "Too many requests for a verification code. Please try again in 842 seconds",
  "class": "business"
}

{
  "status": 500,
  "message": "Cache operation error",
  "code": "otp_m.cache_error",
  "class": "business"
}

{
  "overall_status": "unhealthy",
  "message": "Service is shutting down",
  "timestamp": "2026-08-27T15:04:05Z"
}

POSTInitiate registration

AUTHENTICATED BY X-API-Key, not by a bearer token. auth.APIKeyMiddleware REPLACES the usual chain on this route, so there is no user context, no RBAC check and no licence check — and the refusals are common.api_key_required / common.invalid_api_key rather than common.unauthorized / common.rbac_no_rec_access. An OPTIONS request skips the key check entirely. Begin registration. The answer is deliberately uniform: the same `200` and the same message whether the credential is free, belongs to a registration nobody finished, or is already confirmed on another account. No code is sent for a credential somebody else holds, and no account is created for it; its owner is notified that an attempt was made. Send pacing applies either way, so a burst answers `429 otp_m.resend_too_soon` or `otp_m.resend_limit_exceeded` on any of those paths. `400` covers the request itself — a malformed or undeliverable credential, a password that fails policy, terms or privacy not accepted.

POSTVerify registration

AUTHENTICATED BY X-API-Key, not by a bearer token. auth.APIKeyMiddleware REPLACES the usual chain on this route, so there is no user context, no RBAC check and no licence check — and the refusals are common.api_key_required / common.invalid_api_key rather than common.unauthorized / common.rbac_no_rec_access. An OPTIONS request skips the key check entirely. Submit the registration code. A code checked against an address nobody registered, or one already confirmed, is refused exactly as a wrong code is — `400 otp_m.otp_invalid`, `400 otp_m.otp_expired`, or `429 otp_m.cooling_period_active` once the attempt budget is spent — so the refusal names no account state. Attempts against a credential the caller does not own never reach that credential's budget.