Messaging
Messages, events, and logs
Inspect public message state, normalized events, and webhook delivery summaries without exposing provider payloads.
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.
| Resource | SDK | Scope | Use it for |
|---|---|---|---|
Messages | client.messages | messages:read | Queued, sent, inbound, delivered, read, failed, canceled, and reconciliation-needed message state. |
Events | client.events | events:read | Normalized events and their public message, Sender, or conversation projection. |
Webhook deliveries | client.webhookDeliveries | webhooks:read | Delivery 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.
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)
}| SDK parameter | REST query | Values |
|---|---|---|
direction | direction | inbound or outbound |
from / to | from / to | E.164 phone number |
status | status | queued, sending, sent, delivered, read, failed, canceled, needs_reconciliation |
type | type | text, template, image, video, document, audio, location, contact_card |
createdAfter / createdBefore | created_after / created_before | ISO 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.
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)
}Use the stable event id to deduplicate webhook work and correlate a delivery attempt with the retained event. Event retrieval is an inspection path, not a replacement for webhook processing.
Correlate a send across resources
- Persist the msg_ or msg_test_ ID and req_ request ID returned by the send.
- Retrieve the message to observe its current lifecycle status and failure summary.
- Match event.data.message.id to the message ID when processing or inspecting events.
- Use event.id to inspect webhook delivery summaries when diagnosing endpoint delivery.
- 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
- Prefer signed webhooks for low-latency lifecycle updates.
- Use list APIs for reconciliation, support tooling, and bounded backfills.
- Filter by createdAfter and createdBefore instead of repeatedly scanning all retained history.
- Stop pagination when hasMore is false and store your own reconciliation checkpoint.
- Apply the rate-limit headers from every response and avoid concurrent workers scanning the same window.