CorebanqCorebanq Developer Docs
Usersv1Credentials

Add credential

Add a credential to a user. A value already confirmed on another account answers exactly as a free one does, with the same `201` and the same body, and is stored unvalidated exactly as a free one is — the partial unique index permits the row, and no code that could confirm it is ever sent. The owner of that value is notified instead. A value the caller already holds on another credential of their own still answers `409 users_m.duplicate_credential` — that is their own account. Creating a credential for another user requires the caller to be a scoped administrator in the target's customer context, otherwise `403`, and `404` when the target does not exist. With `send_otp: true` the endpoint issues a one-time code, so the OTP refusals reach this path: `429 otp_m.cooling_period_active`, `500 otp_m.failed_send_otp`, `500 otp_m.cache_error`. Send pacing is the exception: it issues no second code but still answers success while the credential holds a usable one, with `code_sent: false` and the `retry_after` to wait. Only where no code is left does it answer `429 otp_m.resend_too_soon` or `429 otp_m.resend_limit_exceeded`.

POST
/v1/users/credentials

Authorization

bearerAuth
AuthorizationBearer <token>

In: header

Request Body

application/json

TypeScript Definitions

Use the request body type in TypeScript.

Response Body

application/json

application/json

application/json

application/json

application/json

application/json

application/json

application/json

application/json

curl -X POST "https://example.com/v1/users/credentials" \  -H "Content-Type: application/json" \  -d '{    "type": "email",    "value": "string"  }'
{
  "credential": {
    "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",
    "user_id": "a169451c-8525-4352-b8ca-070dd449a1a5",
    "type": "email",
    "value": "string",
    "validated": true,
    "preferred": true,
    "active": true,
    "metadata": {},
    "created_at": "2019-08-24T14:15:22Z",
    "created_by": "ee824cad-d7a6-4f48-87dc-e8461a9201c4",
    "modified_at": "2019-08-24T14:15:22Z",
    "modified_by": "e8d4374d-93a1-4e98-a6c6-fdcf00c5059f"
  },
  "message": "string",
  "code_sent": true,
  "retry_after": 30
}
{
  "status": 400,
  "message": "Invalid user input",
  "code": "users_m.invalid_user_input",
  "class": "validation"
}
{
  "status": 401,
  "message": "Unauthorized",
  "code": "common.unauthorized",
  "class": "business"
}
{
  "status": 403,
  "message": "No access to the record",
  "code": "common.rbac_no_rec_access",
  "class": "business"
}
{
  "status": 404,
  "message": "User not found",
  "code": "common.user_not_found",
  "class": "business"
}
{
  "status": 409,
  "message": "Credential already exists",
  "code": "users_m.duplicate_credential",
  "class": "business"
}

{
  "status": 429,
  "code": "otp_m.cooling_period_active",
  "message": "In cooling period for 1 minute, 60 second(s) left",
  "class": "temporary",
  "retryable": true
}

{
  "status": 500,
  "message": "Internal server error",
  "code": "common.server_error",
  "class": "business"
}

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