Errors
Shape
{
"status": "fail",
"success": false,
"message": "Lead not found.",
"warning": ""
}
status is "fail" for 4xx and "error" for 5xx. A stack field appears only when the server runs with NODE_ENV=development.
Status codes
| Code | Meaning | What to do |
|---|---|---|
400 | Validation failure, a missing required field, a duplicate, or a business-rule refusal. The most common error by far. | Read message; fix the request. |
401 | Missing, unknown or malformed API key — ExpiredSession_401. | Check the apikey header. |
403 | Refused action. | Not usually reachable with an API key, since role and plan gating are skipped. |
404 | No such resource, or no such path. | Check the id and the path. |
409 | Conflict — a uniqueness constraint. | |
429 | Usage limit reached (e.g. AIUsageLimitReached_429). | Back off; check plan usage. |
500 | Unhandled server error. | Retry once, then report it. |
:::note A lapsed subscription currently surfaces as a 500
authenticateAPIKey reads the plan off the company's active subscription without checking that one exists, so a company with no active subscription fails with a 500 and a message like Cannot read properties of null (reading 'planDetails') rather than a clean SubscriptionExpired_400. If you see that, check the subscription before looking anywhere else.
:::
:::note 400 where you might expect 404
Many "not found" cases in LeadX are modelled as 400, not 404 — LeadNotFound_400 and ContactNotFound_400 both exist alongside 404 variants. Do not branch on 404 to detect a missing record. Match on the message, or treat any 4xx as "this request will not succeed as written".
:::
Mongoose-level errors
Some failures are translated by the error handler rather than raised as named messages:
| Trigger | Status | message |
|---|---|---|
| Duplicate key | 400 | Duplicate field(s): email |
| Schema validation | 400 | Validation error: ... |
| Bad ObjectId or type | 400 | Invalid format for field lead: abc |
| Bad/expired JWT (session path only) | 401 | Invalid token. Please log in again |
Named response messages
Internally, messages are looked up by a name like LeadCreated_201 or CampaignNotFound_404 in a ResponseMessage collection, which supplies both the text and the HTTP status. The numeric suffix reflects the intended status, but the stored record is authoritative — text is editable per deployment, so match on status codes and your own logic rather than parsing message strings.
If no record matches the name, the server falls back to 500 / "Something went wrong".
Handling errors well
const leadx = async (path, init = {}) => {
const res = await fetch(BASE + path, {
...init,
headers: { apikey: KEY, "Content-Type": "application/json", ...init.headers },
});
const body = await res.json().catch(() => ({}));
if (!res.ok) {
const err = new Error(body.message || `HTTP ${res.status}`);
err.status = res.status;
err.retryable = res.status >= 500 || res.status === 429;
throw err;
}
return body;
};
Retry 5xx and 429 with backoff. Never retry a 400 — it will fail identically.
:::warning Bulk endpoints return 200 with failures inside
POST /lead/bulk and POST /contact/bulk report per-item failures in the body while returning 200. Check data.failed and data.errors. See Bulk operations.
:::
Error name catalogue
Every non-success name currently raised by the API, grouped by the status its name encodes. Useful for mapping messages back to a cause during debugging.
400
ApiNotFound_400 · AppNotFound_400 · BadRequest_400 · ButtonExampleRequired_400 · ButtonTextTooLong_400 · CallLogNotFound_400 · CampaignAudienceEmpty_400 · CampaignCannotBeUpdated_400 · CampaignFailedManyTime_400 · CampaignNameAlreadyExists_400 · CampaignSMTPNotBelongToCompany_400 · CampaignSMTPNotFound_400 · CampaignSMTPRequired_400 · CampaignSegmentNotFound_400 · CampaignSegmentsRequired_400 · CampaignTemplateRequired_400 · CannotDeleteDefaultTeam_400 · CannotUpdateApprovedTemplate_400 · CannotUpdateUniqueKey_400 · ChatWidgetNameAlreadyExists_400 · ChatWidgetNotFound_400 · ClaudeConfigurationNotFound_400 · ClaudeNotAvailableInPlan_400 · ContactAlreadyExists_400 · ContactCategoryNameAlreadyExists_400 · ContactCategoryNotFound_400 · ContactDataRequired_400 · ContactNotFound_400 · CountryNotFound_400 · CouponInvalid_400 · CustomFieldNameAlreadyExists_400 · CustomFieldNotFound_400 · CustomSectionNameAlreadyExists_400 · CustomSectionNotFound_400 · DashboardNameAlreadyExists_400 · DomainTrackingNameAlreadyExists_400 · DomainTrackingNotFound_400 · DurationRequired_400 · EmptyFile_400 · FacebookAppIDRequired_400 · FacebookSecretRequired_400 · FacebookTokenRequired_400 · FileNotFound_400 · FileUploadFailed_400 · FormFieldsRequired_400 · FormIdOrTokenRequired_400 · FormIdRequired_400 · FormNameAlreadyExists_400 · FormNotFound_400 · FreePlanAlreadyUsed_400 · GeminiConfigurationNotFound_400 · GeminiNotAvailableInPlan_400 · GmailCodeRequired_400 · ImportAlreadyPaused_400 · ImportAlreadyRunning_400 · ImportFileFetchFailed_400 · ImportNotFound_400 · InstagramNoAccessToken_400 · InvalidAIType_400 · InvalidCampaignType_400 · InvalidClaudeConfiguration_400 · InvalidCredentials_400 · InvalidCustomFieldValue_400 · InvalidCustomField_400 · InvalidDataFormat_400 · InvalidEmailTemplate_400 · InvalidGeminiConfiguration_400 · InvalidImportStatus_400 · InvalidInput_400 · InvalidMapping_400 · InvalidMemberStatus_400 · InvalidPanel_400 · InvalidPayload_400 · InvalidRequest_400 · InvalidSectionId_400 · InvalidSystemField_400 · InvalidTemplateStatus_400 · InvalidVariables_400 · InvalidWhatsAppTemplate_400 · InvoiceNotGenerated_400 · JobIdRequired_400 · LeadIdOrTokenRequired_400 · LeadIdTokenStatusRequired_400 · LeadNotFound_400 · ManualPaymentDetailsNotFound_400 · MaximumLeadsExceeded_400 · MemberAlredyExists_400 · MemberNotAccepted_400 · MemberNotBlocked_400 · MemberNotInvited_400 · NoBrevoSelected_400 · NoCampaignSelected_400 · NoChatWidgetSelected_400 · NoCustomSectionSelected_400 · NoDefaultClaudeConfiguration_400 · NoDefaultGeminiConfiguration_400 · NoElasticemailSelected_400 · NoFile_400 · NoFormSelected_400 · NoGeminiConfigurationFound_400 · NoMailchimpSelected_400 · NoMessagesSent_400 · NoPostmarkSelected_400 · NoRecipientsSpecified_400 · NoRowsInFile_400 · NoSESSelected_400 · NoSMTPSelected_400 · NoSendgridSelected_400 · NoSparkpostSelected_400 · NoTeamSelected_400 · PageIdOrTokenRequired_400 · PageIdRequired_400 · PaymentNotFound_400 · PaypalDetailsNotFound_400 · PayuDetailsNotFound_400 · PhonepeDetailsNotFound_400 · PlanDoesNotIncludeThisFeature_400 · PlanMaxUsers_400 · PlanMinUsers_400 · PlanNotFound_400 · ProductServiceDataRequired_400 · ProductServiceNameAlreadyExists_400 · ProductServiceNotFound_400 · PromptRequired_400 · QueueLimitExceeded_400 · RazorpayDetailsNotFound_400 · RoleNameAlreadyExists_400 · RoleOrPermissionRequired_400 · RowFromGreaterThanRowTo_400 · RowFromOutOfRange_400 · SegmentNameAlreadyExists_400 · SegmentNotFound_400 · SettingsNotFound_400 · StateNotFound_400 · StripeDetailsNotFound_400 · SubscriptionExpired_400 · SubscriptionNotFound_400 · TagNameAlreadyExists_400 · TagNotFound_400 · TeamNameAlreadyExists_400 · TeamNotFound_400 · TemplateNameAlreadyExists_400 · UniqueKeyInvalid_400 · UnsupportedCampaignType_400 · UnsupportedFileType_400 · ValidationError_400 · transactionHistoryNotFound_400
401
ExpiredSession_401 · Unauthorized_401
403
PlanLimitExceeded_403 · PlanSubscriptionLimitExceeded_403 · UnauthorizedAction_403
404
BrevoNotFound_404 · CampaignNotFound_404 · ContactNotFound_404 · DashboardNotFound_404 · DefaultRoleNotFound_404 · ElasticemailNotFound_404 · EmailTemplateNotFound_404 · FileNotFound_404 · GmailNotFound_404 · InstagramAccountNotFound_404 · InstagramAppNotFound_404 · LeadActivityNotFound_404 · LeadNotFound_404 · MailchimpNotFound_404 · ManualCampaignNotFound_404 · MemberNotFound_404 · MetaTemplateNotFound_404 · NotFound_404 · OcrNotFound_404 · PostmarkNotFound_404 · ProductServiceNotFound_404 · ReportNotFound_404 · RoleNotFound_404 · SESNotFound_404 · SMTPNotFound_404 · SendgridNotFound_404 · SettingsNotFound_404 · SmtpNotFound_404 · SparkpostNotFound_404 · TelegramAppNotFound_404 · TemplateNotFound_404 · TimezoneNotFound_404 · WhatsAppTemplateNotFound_404 · WhatsappNotFound_404 · WidgetNotFound_404
409
DuplicateTimezone_409
429
AIUsageLimitReached_429
500
AIResponseParsingFailed_500 · AIResponseValidationFailed_500 · DefaultTeamNotFound_500 · EmailSendingFailed_500 · WebhookSubscriptionFailed_500