Error Reference
Every error a referral partner integration can encounter, organized by HTTP status code.
For the standard error response format and retry strategy, see Error Handling. For symptom-based debugging, see Troubleshooting.
400 Bad Request
Validation failures. The request body or query parameters are invalid. Fix the input and retry.
Response format: Varies by endpoint. Field-validation errors are typically a JSON object keyed by field name with arrays of error strings. Value-level business validations return the structured ApiErrorResponseDto shape ({ error, message, businessErrors: [] }). Malformed JSON or type-mismatch errors from model binding return ASP.NET ProblemDetails.
{
"Users": ["At least one user is required"]
}
Client Onboarding (POST /clients)
| Error | Cause |
|---|---|
Request cannot be null | Missing or empty request body |
At least one user is required | users array is null or empty |
When multiple users provided, exactly one must have IsOnboardingUser=true | Multiple users, none marked as onboarding user |
Only one user can be marked as IsOnboardingUser | Multiple users have isOnboardingUser: true |
Unknown country: XX | client.country is not a valid ISO 3166-1 alpha-2 code or country name |
Case validation failed (with validationErrors object) | Cases included in the request have invalid fields — check each Cases[n].Field key |
Case Submission (POST /cases)
| Error | Cause |
|---|---|
Invalid currency code: XYZ | currencyCode not recognized |
Case date cannot be in the future | date is a future date |
Due date cannot be earlier than the case date | dueDate is before date |
Due date must be in the past | dueDate is strictly in the future (tomorrow or later) — today is accepted |
Case total must be a positive amount | amountToRecover is zero or negative |
Debtor information is required | debtor object is null |
Invalid debtor type | debtor.type is not "Company" or "Private" (case-insensitive) |
Invalid country code: ZZ | debtor.countryAlpha2 is not a valid ISO alpha-2 code. If using debtor.country (name), the error message will suggest using countryAlpha2 instead. |
A state code ... is required for US debtors | US debtor without debtor.stateAlpha2 or debtor.state |
Collection partner not found | collectionPartnerId override does not exist or is not active |
Webhooks
| Error | Cause |
|---|---|
| Model state errors | Missing url or events on POST /v1/webhooks |
| URL validation error | Webhook URL is HTTP, localhost, private IP, or uses a non-standard port (only 443/8443 allowed) |
'<type>' is not a valid referral partner event type. Valid types: ... | Unknown event type on POST /v1/webhooks/test. Valid types: case.created, case.updated, case.closed, client.onboarding.poa_signed, client.onboarding.contract_signed, client.linked, client.link_declined, client.link_requested, client.link_expired |
caseId is required for <type> events. | Case event test trigger without caseId |
externalTenantId is required for <type> events. | Client event test trigger without externalTenantId |
The three POST /v1/webhooks/test validation errors above return the standard ApiErrorResponseDto / businessErrors shape, not Problem Details.
401 Unauthorized
Authentication failed. Check your credentials and environment.
Response format: RFC 7807 Problem Details.
{
"title": "Unauthorized",
"detail": "API Key was not provided",
"status": 401
}
| Detail message | Cause | Fix |
|---|---|---|
API Key was not provided | Missing XApiKey header | Add XApiKey: YOUR_KEY to every request |
Invalid API Key | Key not found in Debitura | Verify key is correct and matches the environment (test vs production) |
Invalid bearer token | Token expired (30-min lifetime), revoked, or malformed | Mint a fresh token via POST /oauth/token. See the retry-on-401 pattern. |
403 Forbidden
Test-only endpoints called against production.
{
"title": "Forbidden",
"detail": "This endpoint is only available in the test environment.",
"status": 403
}
Affected endpoints:
DELETE /clients/{externalTenantId}— reset test clientsGET /clients/{externalTenantId}/is-deletablePOST /v1/webhooks/test— webhook testing
404 Not Found
The requested resource does not exist or does not belong to your partner account.
| Endpoint | Title | Common cause |
|---|---|---|
GET/DELETE /clients/{externalTenantId} | Client Not Found | Wrong externalTenantId, client archived, or belongs to another partner |
POST /oauth/token | Client Not Found | No active link for this externalTenantId — the client was never created, or the link was archived/withdrawn. A token is minted as soon as a non-deleted, non-archived link exists; SDCA does not need to be signed first. |
GET /cases/{id} | Case Not Found | Case does not exist or belongs to a client not linked to your partner account |
GET/PUT/DELETE /v1/webhooks/{id} | Webhook not found | Webhook belongs to a different partner — list webhooks via GET /v1/webhooks to find valid IDs |
409 Conflict
A conflict with existing state. Each conflict type requires different handling.
Response format: type, message, and optional data payload.
ClientExistsNeedsLinking
The email matches an existing Debitura account. Present data.onboardingLinks.url to the user for approval. Approval URLs expire after a configurable per-partner TTL (ApprovalTtlDays, default 7 days, clamped 1–30 days). Subscribe to client.link_expired to detect expiry; resubmit POST /clients with the same externalTenantId to generate a fresh approval URL.
Cases submitted in the original request are persisted server-side and replayed against the matched creditor automatically once the user approves the link. AllowPendingContracts=true is forced on replay regardless of what you sent on the original request, so cases land in PendingContractSigning lifecycle state rather than 422-failing on missing SDCA/PoA/KYC. There is no case.failed webhook on this path — detect failures via the case lifecycle field. See Handling 409 Conflicts for the full flow.
{
"type": "ClientExistsNeedsLinking",
"message": "Client already exists in Debitura. Approval required to link to referral partner.",
"data": {
"externalTenantId": "your-tenant-id",
"onboardingLinks": {
"url": "https://app.debitura.com/ReferralPartner/Approve?token=..."
}
}
}
For privacy, the 409 response does not reveal any details about the user's matched Debitura account — externalTenantId and onboardingLinks.url are the only fields you need to act on. The response also includes client, users, and isAttributedClient for JSON-shape stability with the success response — do not rely on them being populated on this conflict path.
ClientAlreadyLinkedToAnotherPartner
The client has an active link to a different referral partner. Only one partner per creditor. Contact partnerships@debitura.com.
InvalidClientType
InvalidClientType is defined in the conflict-type enum but is not currently returned. When the matched account is not a creditor — for example, a collection partner — the request surfaces as a 500 Internal Server Error (no type discriminator), not a 409. This is treated as a data-integrity anomaly that should not occur in practice. Contact partnerships@debitura.com if you hit it. See Handling 409 Conflicts.
Approve / decline race (link request already processed)
Concurrent attempts to approve or decline the same link request return 409 Conflict (atomic claim). Only the first caller succeeds; subsequent callers should treat the conflict as "already processed." This typically appears as a user double-clicking the approval button or two browser tabs racing the same approval URL — your integration does not need to retry.
Withdrawal Not Allowed
POST /clients/{externalTenantId}/withdraw returns Problem Details (not the type/message format above) when withdrawal is blocked — either because isAttributedClient is false, or cases have progressed beyond PendingContractSigning.
{
"title": "Withdrawal Not Allowed",
"detail": "...",
"status": 409
}
422 Unprocessable Entity
Business rule violations. The request is valid but cannot be processed due to domain constraints.
Response format: businessErrors array with type, message, and optional solutionUrl.
{
"businessErrors": [
{
"type": "MissingPowerOfAttorney",
"message": "Power of attorney not signed with partner X.",
"solutionUrl": "https://referral.debitura.com/PowerOfAttorney/Sign/..."
}
]
}
| Type | Cause | Has solutionUrl | Fix |
|---|---|---|---|
MissingDebtCollectionContract | SDCA not signed or needs re-signing | Yes | Present URL to client, retry after signing |
MissingPowerOfAttorney | PoA (or JPA for a jurisdiction pricing zone) not signed for this jurisdiction | Yes | Present URL to client, retry after signing |
MissingKycVerification | Collection partner requires KYC and creditor hasn't completed it | Yes | Present URL to client, retry after completion. Not bypassed by allowPendingContracts. |
NoPartnerAvailable | No collection partner covers this jurisdiction | No | Show "not supported" message. Do not auto-retry. |
CreditorBlocked | Creditor account is blocked | No | Contact Debitura support. Error message includes the reason. |
Unlike contract-related errors, allowPendingContracts: true does not bypass MissingKycVerification. KYC is always enforced when submitting via bearer token. See KYC Verification.
429 Too Many Requests
Rate limit exceeded. Read retryAfter from the response body and wait.
{
"error": "rate_limit_exceeded",
"message": "Too many requests. Please try again later or contact support if you need higher limits.",
"retryAfter": 42.0
}
Limits per API key: 2,000 requests/minute, 20,000/hour, 100,000/day. See Rate Limiting.
500 Internal Server Error
Transient server error. Retry with exponential backoff (max 3 retries). If persistent, contact partnerships@debitura.com with the request details and timestamp.