Status history
History records actual status changes. Use it to recover their order as well as to understand the current value.
New features are enabled for your organization on request.
#Three routes
GET /v1/leads/{id}/status-history: a lead's participation history in a campaign; requiresleads:read.GET /v1/campaigns/{id}/status-history: campaign history; requirescampaigns:read.GET /v1/campaigns/{id}/accounts/{account_id}/status-history: account history in the selected campaign; requiresaccounts:read.
Account history does not expose activity in other campaigns. The key's workspace and campaign scope applies. An out-of-scope entity returns 404, just like a missing entity.
curl -G https://api.melioralab.io/v1/leads/5a7c3e92-1f4b-4d68-9c2e-7b0a6d4f8e31/status-history \
-H "Authorization: Bearer $MELIORA_API_KEY" \
-d limit=50Each route accepts limit and cursor. The default is 50 records and the maximum is 100. Transitions are ordered by ascending entity_revision, not notification delivery time. A cursor lasts 15 minutes. On 410, start again and merge records by id. See the history reference for full responses.
#Transition records
Every item in data has object=status_transition and includes:
id: the transition ID;entity_kind:lead,campaignoraccount;entity_id: that entity's ID.workspace_id,campaign_id,lead_id: context.lead_idmay benullin campaign and account history.entity_revision: the entity's transition sequence number, starting at 1. Use it to order changes within one entity's history.from_status,to_status: the previous and new public statuses.occurred_at: when the change happened;recorded_at: when it was saved.reason_code:operator_action,schedule,queue_exhausted,recipient_not_found,telegram_auth,subscription,recoveryorunspecified.source: who changed the status:user(a cabinet user),system(service automation),support(Meliora support) orunknown. This is informational, not an instruction to the client.
An unavailable reason is returned as unspecified; an unavailable source is unknown. Saving the same status does not create another transition. History describes changes to the status field, not every action on the entity.
#Public statuses
For leads: new, contacted, completed, replied, blocked, failed, suspended, deleted, opted_out, unknown. See lead statuses for their meanings.
For campaigns: draft, scheduled, active, paused, stopped, completed, archived, launching, failed, paused_no_credits, unknown. See campaign statuses.
For accounts in history:
online: connected;offline: disconnected.limited: restricted;banned: banned.deleted: deleted;locked: access locked;unauthorized: authorization required.paused: paused;unknown: the value could not be determined.
Unrecognized source values become unknown. This is valid for both from_status and to_status; do not turn it into failed or offline. Account history includes paused; the ordinary account list may represent that value as unknown.
Statuses can return to earlier values: for example, paused can change to active. A rule that only allows statuses to move forward is not suitable for this history.
#Initial snapshot and completeness
meta.baseline contains a separate status_baseline object with status, entity_revision, recorded_at, entity context and provenance=baseline, or is null. Its revision may be 0. This is an observed initial status, not a transition from an invented previous value. It does not produce a *.status_changed event.
History boundaries are in meta:
history_started_at: when history collection began. Earlier changes are not reconstructed.available_from: the beginning of the available history.retained_from: the beginning of the retained period.observed_at: when the server read the data.
Unknown boundaries are null. Expiration of older records must not be mistaken for complete history since the entity was created.
meta.completeness.status is complete, partial or unknown. It also includes gaps, gaps_truncated and incomplete_from. Each gap has id, from, to and state; states are open, recovering, recovered, unrecoverable and unknown. The list is limited to 50 intervals; gaps_truncated=true means it was shortened. incomplete_from helps identify earlier incompleteness; null alone does not prove there are no gaps.
complete refers to the available history period, not the entity's entire lifetime. For partial or unknown, preserve the incomplete state, reconcile the current record and read history again later. has_more=false only means there are no more pages.
#Status change events
The feed supports lead.status_changed, campaign.status_changed and account.status_changed. Select the 2b catalog or one explicit new type.
data includes organization_id, workspace, campaign, transition_id, entity_kind, entity_id, from_status, to_status, reason_code, source, occurred_at and recorded_at. transition_id matches the transition's id in history. An account event belongs to one campaign; it does not list other campaigns.
lead.status_changed only reports a status change. lead.replied reports a counted business reply: each replying lead is counted once per campaign. Do not increment reply counts from lead.status_changed or every message.received; the latter represents one individual incoming message. A single reply can produce different event types describing the same interaction.
Deduplicate repeated deliveries by event.id. Notifications can arrive after the change itself. To recover the order, read history by entity_revision and match transition_id. Reconcile the record for its current state. Do not choose the final status based only on webhook arrival order.
New webhooks of these types carry metadata only. A status change does not include message text.