Errors

The shape of an Emelia API error, what each HTTP status means today, the stable error codes, and how to retry safely.

The shape of an error

The main API answers errors with a JSON body that always has an error message. Catalogued errors also carry a stable code, and sometimes params:

{
  "error": "You cannot add more than 40 LinkedIn contacts per day when your sequence contains a connection request",
  "code": "MC_LINKEDIN_DAILY_LIMIT_CONNECTION",
  "params": { "limit": 40 }
}

Branch on code when there is one, never on the text of error: messages can be reworded, codes are never renamed. Most business errors have no code yet: rely on the HTTP status and show the message.

The lists and contacts routes (/lists/...) are served by another service and use a message field instead, sometimes with a code:

{ "message": "Request body too large: the limit is 5mb", "code": "PAYLOAD_TOO_LARGE", "params": { "limit": "5mb" } }

Their 401 is the exception: { "error": "Authentication required", "code": "AUTH_REQUIRED" }.

Status codes

StatusMeaning todayWhat to do
200, 201Success. Adding a contact that already exists also answers 200: read duplicate in the body.
400The request is invalid, a business rule refused it, or the resource was not found: the main API answers "not found" with a 400, for example {"error": "Campaign not found"}. Validation errors carry code: "INVALID_REQUEST" and name the field, for example "\"country\" is required". A malformed JSON body gets an HTML 400 page.Fix the request. Do not retry it unchanged.
401Missing, unknown or disabled API key: {"error": "Authentication required"}.See Authentication.
404Lists and contacts: the list or contact does not exist, or belongs to another account ({"message": "List not found"}). Any route: an unknown path, answered with an HTML page.Check the id, the path and the method.
409ACCOUNT_DISCONNECTED: the LinkedIn account used by the request is disconnected.Reconnect the account in the app.
413Lists and contacts: the body is larger than 5 MB (PAYLOAD_TOO_LARGE).Send fewer contacts per call.
422Lists and contacts: the body is well formed but not acceptable (a field holds an array, a batch has more than 100 contacts, an email and a LinkedIn URL in the same lookup).Read message.
429RATE_LIMITED: LinkedIn itself slows the account down.Retry later.
500Your per minute quota is exceeded ({"error": "Internal server error"}, no Retry-After), or an unexpected server error.Wait for the next minute, then retry slower. See Rate limits.
503STATS_COMPUTING: the statistics of the campaign are being computed, with Retry-After: 30.Retry after the delay.

Routes with their own format

  • The statistics routes (/stats/...) answer 401 when the campaign is not found, and 401 {"error": "You must be logged in"} on any internal error. A 401 there does not always mean a bad key: check the campaign id first.
  • GET /webhook answers 401 {"error": "You must be logged in"} when reading the webhooks fails.
  • GET /credits/usage answers {"success": false, "error": "..."}.
  • The deliverability diagnosis (POST /email-providers/warmup/check-health) answers {"success": false, "message": "..."}.
  • The message of a send activity answers 404 {"error": "NOT_FOUND"} or 422 {"error": "NO_MESSAGE"}.
  • The LinkedIn scraper routes answer English sentences without code, all with a 400.

Out of credits

Enrichment calls fail with a 400 and this message when your balance is too low:

{ "error": "You do not have enough credits to perform this action" }

Stop the batch rather than retrying row by row, and top up your credits. See Credits.

Error codes

Starting a campaign

POST /advanced/campaigns/{campaignId}/start runs these checks in this order, and stops at the first that fails:

CodeMeaning
SUBSCRIPTION_REQUIREDA campaign needs an active subscription to start.
CAMPAIGN_NOT_FOUNDNo campaign with this id on your account.
NO_IDENTITY_SETThe campaign has no identity (sender).
IDENTITY_FOREIGN_ACCOUNTAn identity uses an account that does not belong to you.
LINKEDIN_AUDIO_MISSINGA LinkedIn voice note step has no recording.
EMAIL_STEP_NO_ACTIVE_VERSIONAn email step has no active version.
EMAIL_VERSION_NO_CONTENTA version of an email step is empty.
EMAIL_VERSION_NO_SUBJECTThe first email of the sequence needs a subject.
LINKEDIN_COOKIE_EXPIRED, LINKEDIN_SLOT_EXPIREDReconnect the LinkedIn account, or renew its slot.
LINKEDIN_ACCOUNT_AMBIGUOUSUse a single LinkedIn account for all identities, or one per identity.
EMAIL_PROVIDER_DISCONNECTED, EMAIL_PROVIDER_EXPIREDA mailbox is disconnected, or its subscription expired.
IDENTITY_EMAIL_PROVIDER_MISSINGThe sequence has an email step, so every identity needs an email provider.
WHATSAPP_ACCOUNT_EXPIRED, WHATSAPP_SLOT_EXPIRED, WHATSAPP_ACCOUNT_AMBIGUOUSThe same checks for WhatsApp accounts.
SCHEDULE_MISSINGThe campaign has no schedule.
MC_LINKEDIN_DAILY_LIMIT_CONNECTIONMore than 40 new LinkedIn contacts per day with an invitation in the sequence.
MC_LINKEDIN_DAILY_LIMITMore than 80 new LinkedIn contacts per day.
NO_RECIPIENTSNo contact in the attached lists.
TOO_MANY_RECIPIENTSMore than 30,000 recipients.
CAMPAIGN_ALREADY_RUNNINGThe campaign is already running.
RECIPIENTS_NO_EMAILThe sequence has an email step but no contact has an email.
RECIPIENTS_NO_LINKEDINThe sequence has a LinkedIn step but no contact has a LinkedIn profile URL.

Other codes

CodeMeaning
INVALID_REQUESTThe body or the query failed validation. The message names the field.
INVALID_PARAMETERSInvalid parameters.
PLANNED_START_INVALID, PLANNED_START_IN_PASTplannedStart is not a valid date, or is in the past.
EMAIL_PROVIDER_MISSING, LINKEDIN_ACCOUNT_MISSINGNo mailbox, or no LinkedIn account, to act with.
TRACKING_DOMAINS_REQUIREDEvery email provider needs a tracking domain for this campaign.
WARMUP_PROVIDER_UNSUPPORTEDWarmup cannot be enabled on this provider.
ACCOUNT_DISCONNECTEDThe LinkedIn account is disconnected (409).
RATE_LIMITEDLinkedIn slows the account down (429).
STATS_COMPUTINGStatistics are being computed (503).

Retrying safely

  • Retry 500 and 503 after a pause (60 seconds for the quota, the Retry-After value otherwise), a few times at most.
  • Do not retry a 4xx unchanged: the answer will be the same.
  • Reads (GET) are safe to retry.
  • Adding a contact to a list is safe to retry when the contact has an email or a LinkedIn URL: the duplicate is detected. Without either, every retry creates a new contact.
  • Never retry a request that started a paid job (Email Finder, Phone Finder, Email Verifier) without checking the job first: you would pay twice.

On this page