Browse documentation

Messaging

Messages, events, and logs

Inspect public message state, normalized events, and webhook delivery summaries without exposing provider payloads.

DevelopersAdvancedAvailable

What this guide covers

  • Use message logs for lifecycle state and event logs for normalized business events.
  • Correlate public message, event, delivery, and request IDs across operations.
  • Filter and paginate within the API key's project, environment, and Sender scope.

Choose the log that answers your question

Yanola exposes message logs for message lifecycle state, event logs for normalized business events, and webhook delivery summaries for endpoint attempts. These are separate resources with separate read scopes.

Public log resources
ResourceSDKScopeUse it for
Messagesclient.messagesmessages:readQueued, sent, inbound, delivered, read, failed, canceled, and reconciliation-needed message state.
Eventsclient.eventsevents:readNormalized events and their public message, Sender, or conversation projection.
Webhook deliveriesclient.webhookDeliverieswebhooks:readDelivery status, attempt count, response summary, and replay state.

List and retrieve messages

Message logs include API, workflow, dashboard, inbound, and Test Mode sources visible to the key's project, environment, and allowed Senders. They never accept raw provider message IDs.

Filter recent failed outbound messages
ts
import { WhatsAppClient } from "@yanola/whatsapp"

const apiKey = process.env.YANOLA_API_KEY

if (!apiKey) {
  throw new Error("Missing YANOLA_API_KEY")
}

const yanola = new WhatsAppClient({ apiKey })

const failures = await yanola.messages.list({
  direction: "outbound",
  status: "failed",
  from: "+15557654321",
  createdAfter: "2026-07-29T00:00:00.000Z",
  limit: 100,
})

for (const message of failures.data) {
  const current = await yanola.messages.retrieve(message.id)
  console.log(current.id, current.failure)
}
Message list filters
SDK parameterREST queryValues
directiondirectioninbound or outbound
from / tofrom / toE.164 phone number
statusstatusqueued, sending, sent, delivered, read, failed, canceled, needs_reconciliation
typetypetext, template, image, video, document, audio, location, contact_card
createdAfter / createdBeforecreated_after / created_beforeISO 8601 date-time

List and retrieve normalized events

Event data is Yanola's retained normalized public projection. The events API never returns raw Meta payloads or internal raw-payload references.

Inspect recent failed-message events
ts
import { WhatsAppClient } from "@yanola/whatsapp"

const apiKey = process.env.YANOLA_API_KEY

if (!apiKey) {
  throw new Error("Missing YANOLA_API_KEY")
}

const yanola = new WhatsAppClient({ apiKey })

const events = await yanola.events.list({
  type: "whatsapp.message.failed",
  from: "+15557654321",
  createdAfter: "2026-07-29T00:00:00.000Z",
})

for (const event of events.data) {
  const retained = await yanola.events.retrieve(event.id)
  console.log(retained.id, retained.data.message?.id)
}

Correlate a send across resources

  1. Persist the msg_ or msg_test_ ID and req_ request ID returned by the send.
  2. Retrieve the message to observe its current lifecycle status and failure summary.
  3. Match event.data.message.id to the message ID when processing or inspecting events.
  4. Use event.id to inspect webhook delivery summaries when diagnosing endpoint delivery.
  5. Include requestId in support reports; it identifies the API request, while message and event IDs identify durable resources.

Paginate and isolate queries

Message, event, and delivery lists default to 20 records and accept at most 100. Follow nextCursor while hasMore is true and pass the cursor unchanged.

All log reads are isolated by Yanola Account, Agent Project, key environment, and any Sender restriction. An explicit environment must match the key. A cursor from a different filter set or scope is invalid.

Operational polling

  1. Prefer signed webhooks for low-latency lifecycle updates.
  2. Use list APIs for reconciliation, support tooling, and bounded backfills.
  3. Filter by createdAfter and createdBefore instead of repeatedly scanning all retained history.
  4. Stop pagination when hasMore is false and store your own reconciliation checkpoint.
  5. Apply the rate-limit headers from every response and avoid concurrent workers scanning the same window.