Error Codes
Coinepay OpenAPI error responses contain:
code: same as HTTP status (200/400/401/404/409/429/500)message: localized viaAccept-Languagemessage_key: stable English key, use this for programmatic checks
{
"code": 400,
"message": "Idempotency-Key conflict",
"message_key": "openapi_idempotency_key_conflict",
"data": null
}HTTP Status Semantics
| HTTP | Meaning |
|---|---|
| 200 | Success (incl. "idempotent replay" and "queued") |
| 400 | Validation / business-rule failure (use message_key to differentiate) |
| 401 | Auth failure (always openapi_invalid_credentials, no further detail) |
| 403 | Authenticated but feature-gated (e.g. second-factor verification missing/failed for sensitive card info, or card not active) |
| 404 | Resource not found / not owned by current account |
| 409 | Idempotency-Key conflict (same key, different body) |
| 429 | Rate limited |
| 500 | Internal server error |
Full message_key Dictionary
Auth & Credentials
| message_key | en-US | zh-CN | HTTP |
|---|---|---|---|
openapi_invalid_credentials | Invalid OpenAPI credentials | 无效的 OpenAPI 凭证 | 401 |
ID Validation
| message_key | en-US | zh-CN | HTTP |
|---|---|---|---|
openapi_invalid_header_id | Invalid header_id | header_id 不合法 | 400 |
openapi_invalid_package_id | Invalid package_id | package_id 不合法 | 400 |
openapi_invalid_card_id | Invalid card_id | card_id 不合法 | 400 |
Idempotency
| message_key | en-US | zh-CN | HTTP |
|---|---|---|---|
openapi_idempotency_key_required | Missing Idempotency-Key header | 缺少 Idempotency-Key 请求头 | 400 |
openapi_idempotency_key_too_long | Idempotency-Key too long | Idempotency-Key 超长 | 400 |
openapi_idempotency_key_invalid_chars | Invalid characters | 含非法字符 | 400 |
openapi_idempotency_key_conflict | Idempotency-Key conflict | Idempotency-Key 冲突 | 409 |
Business Rules
| message_key | en-US | zh-CN | HTTP |
|---|---|---|---|
openapi_card_type_not_supported | Not supported (virtual cards only) | 暂不支持该卡类型 | 400 |
account_manager_bind_required | Account-manager-controlled header; bind an account manager first | 该卡头需绑定客户经理后才能开通 | 400 |
account_manager_unavailable | Your account manager is no longer active; contact support | 绑定的客户经理已失效,请联系客服 | 400 |
account_manager_open_disabled | Header / package not opened for your account by your account manager | 您的客户经理尚未对该账号开放此卡头 / 套餐 | 400 |
amount_required | amount is required | amount 为必填 | 400 |
card_not_found | Card not found | 卡片不存在 | 404 |
insufficient_balance | Insufficient balance | 余额不足 | 400 |
kyc_required | Please complete identity verification first | 请先完成实名认证 | 400 |
kyc_not_approved | KYC not approved | KYC 未通过 | 400 |
virtual_g_name_invalid | (G card) cardholder name fails the ASCII-letters / single-space / ≤40 chars rule | (G 卡)持卡人姓名不满足规则 | 400 |
virtual_g_required_fields_missing | (G card) server could not assemble a complete apply payload | (G 卡)开卡资料不完整 | 400 |
virtual_a_required_fields_missing | (A card) first_name / last_name missing or blank, or apply payload incomplete | (A 卡)持卡人姓名缺失或开卡资料不完整 | 400 |
First Deposit (Custom Amount)
Returned by /card/apply (as message_key, HTTP 400) and by /card/first_deposit/preview (as invalid_reason inside a 200 body) when a custom first_deposit_amount cannot be honored.
| message_key | en-US | zh-CN | HTTP |
|---|---|---|---|
invalid_first_deposit_amount | first_deposit_amount must be a non-negative integer | first_deposit_amount 必须是非负整数 | 400 |
first_recharge_below_base | First deposit below the config base | 首充低于配置底额 | 400 |
first_recharge_exceeds_max | Excess exceeds max recharge | 超额超过最大充值 | 400 |
first_recharge_limit_exceeded | Excess exceeds the recharge limit | 超额超过充值限额 | 400 |
first_recharge_asset_mismatch | Open-fee asset differs from recharge asset | 开卡费资产与充值资产不一致 | 400 |
first_recharge_excess_too_small | Excess too small after fee + rounding | 超额扣费取整后过小 | 400 |
Sensitive Card Information
These codes are returned by /openapi/card/info when with_sensitive=true cannot be honored, and by /openapi/card/sensitive/send_code. Sensitive fields require the card to be status=2 (active) plus either valid user second-factor codes or a verification-exempt credential.
| message_key | en-US | zh-CN | HTTP |
|---|---|---|---|
email_code_required | Please enter email verification code | 请输入邮箱验证码 | 403 |
sms_code_required | Please enter SMS verification code | 请输入短信验证码 | 403 |
pin_required | Please enter PIN | 请输入 PIN 码 | 403 |
2fa_code_required | Please enter 2FA code | 请输入 2FA 验证码 | 403 |
invalid_email_code | Invalid email verification code | 邮箱验证码错误 | 403 |
invalid_sms_code | Invalid SMS verification code | 短信验证码错误 | 403 |
invalid_pin | Invalid PIN | PIN 码错误 | 403 |
invalid_2fa_code | Invalid 2FA code | 2FA 验证码错误 | 403 |
email_not_verified | Email not verified | 邮箱未验证 | 403 |
phone_not_verified | Phone not verified | 手机号未验证 | 403 |
no_usable_auth_method | No usable authentication method | 无可用的认证方式 | 403 |
card_sensitive_access_banned | Sensitive card info access via API has been disabled by the administrator | 通过 API 获取敏感卡片信息的权限已被管理员关闭 | 403 |
openapi_sensitive_card_only_active | Sensitive card info is only available for active cards. | 仅激活状态的卡片可获取敏感信息 | 403 |
too_many_requests | Too many requests (verification locked / send cooldown; honor Retry-After) | 请求过于频繁(验证锁定 / 发码冷却,遵循 Retry-After) | 429 |
send_code_failed | Failed to send verification code (cooldown released, retry allowed) | 验证码发送失败(冷却已释放,可立即重试) | 500 |
openapi_sensitive_card_info_disabled(403) is no longer returned: credentials without the verification exemption now fall back to the user second-factor path instead of being rejected outright.
Card List Filters
| message_key | en-US | zh-CN | HTTP |
|---|---|---|---|
openapi_conflicting_card_filters | usable_only and statuses cannot be used together | usable_only 与 statuses 不能同时传 | 400 |
openapi_invalid_status_value | statuses contains an invalid value (only 1-6 allowed) | statuses 含非法状态值(仅允许 1-6) | 400 |
invalid_min_balance | min_balance must be a valid non-negative number | 最小余额必须是合法非负数字 | 400 |
invalid_max_balance | max_balance must be a valid non-negative number | 最大余额必须是合法非负数字 | 400 |
min_balance_exceeds_max_balance | min_balance must not exceed max_balance | 最小余额不能大于最大余额 | 400 |
invalid_date_format | date must be in the expected format | 日期格式不正确 | 400 |
Transaction Filters
Returned by /transactions/list and /card/transactions/list.
| message_key | en-US | zh-CN | HTTP |
|---|---|---|---|
invalid_date_format | transaction_time_from/to must be YYYY-MM-DD HH:MM:SS | 交易时间格式必须为 YYYY-MM-DD HH:MM:SS | 400 |
invalid_params | Invalid parameters (e.g. bad amount format) | 参数无效(如金额格式错误) | 400 |
Freeze / Unfreeze
Returned by /card/freeze and /card/unfreeze.
| message_key | en-US | zh-CN | HTTP |
|---|---|---|---|
card_already_frozen | Card is already frozen | 卡片已被冻结 | 400 |
card_status_cannot_freeze | Current card status does not allow freezing | 当前卡状态不可冻结 | 400 |
card_not_frozen | Card is not frozen | 卡片未被冻结 | 400 |
card_type_mismatch | Card type does not match | 卡类型与配置不匹配 | 400 |
operation_not_supported | Operation not supported for this card type | 该操作不支持 | 400 |
recharge_unfreeze_disabled | Recharge-based unfreeze is not available for this card | 该卡不允许通过充值自动解冻 | 400 |
unauthorized_unfreeze_admin | Frozen by admin — cannot self-unfreeze | 该卡片被管理员冻结,用户无法自行解冻 | 403 |
unauthorized_unfreeze_risk | Frozen by risk control — cannot self-unfreeze | 该卡片被风控冻结 | 403 |
unauthorized_unfreeze_system | Frozen by system — cannot self-unfreeze | 该卡片被系统冻结 | 403 |
Close Card
Returned by /card/close (v1.3 — the endpoint has been live since v1.2 but undocumented).
| message_key | en-US | zh-CN | HTTP |
|---|---|---|---|
card_is_closing | Card is being closed | 卡片正在注销中 | 400 |
card_already_closed | Card is already closed | 卡片已注销 | 400 |
card_frozen_cannot_close | Card is frozen and cannot be cancelled | 卡片已冻结,无法注销 | 400 |
card_status_cannot_close | Current card status does not allow cancellation | 当前卡状态不可注销 | 400 |
close_not_accepted_by_upstream | Closure not accepted by the provider; the card has been restored to active — retry later | 销卡未被上游受理,卡片已恢复为可用状态,请稍后重试 | 400 |
card_upstream_closed_pending_reconcile | Card was already closed by the provider and awaits manual refund reconciliation | 该卡已被上游直接注销,正在等待人工对账退款 | 400 |
card_config_not_found | Card configuration not found | 卡配置不存在 | 400 |
openapi_close_card_failed | Failed to close card | 销卡失败 | 500 |
Server Errors
| message_key | en-US | zh-CN | HTTP |
|---|---|---|---|
openapi_internal_error | OpenAPI internal error | OpenAPI 内部错误 | 500 |
openapi_list_card_headers_failed | Failed to list card headers | 查询卡头列表失败 | 500 |
openapi_list_card_configs_failed | Failed to list card configs | 查询卡配置列表失败 | 500 |
openapi_apply_card_failed | Failed to apply virtual card | 申请虚拟卡失败 | 500 |
openapi_first_deposit_preview_failed | Failed to preview first deposit | 首充预览失败 | 500 |
openapi_recharge_failed | Failed to recharge | 充值失败 | 500 |
openapi_get_card_info_failed | Failed to get card info | 获取卡片信息失败 | 500 |
openapi_list_cards_failed | Failed to list cards | 查询卡列表失败 | 500 |
openapi_list_webhook_events_failed | Failed to list webhook events | 查询 Webhook 事件历史失败 | 500 |
openapi_list_transactions_failed | Failed to list transactions | 查询交易明细失败 | 500 |
openapi_freeze_card_failed | Failed to freeze card | 冻结卡片失败 | 500 |
openapi_unfreeze_card_failed | Failed to unfreeze card | 解冻卡片失败 | 500 |
Recommended Handling
def handle_response(resp):
code = resp.get('code', 500)
key = resp.get('message_key', '')
if code == 200:
return resp['data']
if code == 401:
# Always openapi_invalid_credentials
raise AuthError("invalid credentials — check AppID/Secret/clock")
if code == 403:
# Feature-gated: caller is authenticated but lacks permission for this specific request
if key.endswith('_required'):
raise PermissionError(f"Second factor missing ({key}) — "
"call /openapi/card/sensitive/send_code, then resubmit")
if key.startswith('invalid_'):
raise PermissionError(f"Second factor rejected: {key}")
if key == 'openapi_sensitive_card_only_active':
raise RuntimeError("Card must be active before requesting sensitive fields")
raise PermissionError(f"Forbidden: {key}")
if key == 'insufficient_balance':
raise BalanceError()
if key == 'kyc_not_approved' or key == 'kyc_required':
raise KYCError()
if key.startswith('openapi_invalid_') and key.endswith('_id'):
raise ValueError(f"bad ID: {key}")
if key == 'openapi_idempotency_key_conflict':
# Don't auto-retry — caller must check the body
raise IdempotencyConflict()
if code == 429:
raise RateLimited()
if code >= 500:
raise ServerError(resp.get('message', 'internal error'))
raise ApiError(code, key, resp.get('message'))Localization
Set the Accept-Language header to control the message language:
Accept-Language: en-US| Value | Behavior |
|---|---|
| Not sent | Default Chinese |
zh-CN / zh | Chinese |
en-US / en | English |
| Other | Fallback to default |
message_key is independent of Accept-Language and always stable.