List recent messages
Returns the most recent entries saved to any of the account's Voicegrams' conversations, newest first, each as a full `voicegram.message` envelope: the exact s…
Returns the most recent entries saved to any of the account's Voicegrams' conversations, newest first, each as a full voicegram.message envelope: the exact shape a webhook subscription receives. Use it to build a trigger in an automation tool and to see what a delivery looks like. Removed entries are not listed.
Accounts with no entries yet receive a single sample envelope.
Every entry carries sender_verification, its reason and a plain sentence, and an entry Voicegram could not confirm the sender of is listed like any other.
Part of the conversations feature: a workspace it is not available for gets 404 conversation_not_available.
Endpoint
GET /api/v1/messages/recent
Authentication
Bearer Personal Access Token. See Authentication.
Query parameters
| Name | Type | Required | Description |
|---|---|---|---|
direction | enum ("inbound", "outbound") | no | Only entries in this direction. Any other value returns both. |
limit | integer | no | How many entries to return. Defaults to 3. Values outside the allowed range are clamped to the nearest valid value; non-integer or unparseable input falls back to the default. |
Example request
curl "https://www.voicegram.io/api/v1/messages/recent?direction=inbound&limit=3" \
-H "Authorization: Bearer vg_pat_<your-token>"Responses
Recent entries in webhook-payload shape
| Field | Type | Required | Description |
|---|---|---|---|
messages | array of object | yes | |
messages[].event | enum ("voicegram.message") | yes | |
messages[].event_id | string | yes | Stable event identifier of the form evt_<uuid>. The SAME value is sent across retries of the same event. Deduplicate on this field; events can arrive out of order, so sort on data.message.occurred_at. |
messages[].delivered_at | string (date-time) | yes | ISO 8601 timestamp when this delivery attempt fired. |
messages[].data | object | yes | |
messages[].data.id | string | yes | The Voicegram's identifier, the same id every event uses; it matches the id in dashboard_url. The entry's own id is message.id. |
messages[].data.message | object | yes | |
messages[].data.message.id | string | yes | The entry's own identifier. |
messages[].data.message.medium | enum ("email", "sms", "whatsapp", "call", "note") | yes | What kind of entry it is. |
messages[].data.message.direction | string | null | yes | outbound is your side speaking to the sender. inbound is the sender, or someone copied in on an email, speaking to you. Null for a note. |
messages[].data.message.source | enum ("logged", "forwarded") | yes | logged: recorded as it happened. forwarded: an email a member forwarded to the Voicegram's saving address. For a forwarded entry, sender_verification describes the member who forwarded it (logged_by), not whoever wrote the message inside, which Voicegram cannot check: an automation that reads author.type recorder as "the sender replied" should also require source logged. |
messages[].data.message.created_via | enum ("dashboard", "inbound_email", "api") | yes | How the entry reached Voicegram: typed in the dashboard, received at the Voicegram's saving address, or written through this API. |
messages[].data.message.author | object | yes | Who said it. recorder is the person who left the Voicegram. workspace is your side: a signed-in member (name is their display name) or an API token (name is null; see logged_by). participant is someone who was copied in on an email and then wrote in under their own address, which also covers an email from an address Voicegram had not seen before that a member reviewed and kept. The recorder's own address is never here: it is in data.sender. |
messages[].data.message.author.type | enum ("workspace", "recorder", "participant") | yes | |
messages[].data.message.author.name | string | null | yes | A member's display name on a workspace entry. Null on every other entry. |
messages[].data.message.author.address | string | null | yes | The address a participant wrote from. Null on every other entry. |
messages[].data.message.logged_by | object | null | yes | Who saved the entry, when that is someone other than its author: a member who logged or forwarded it, or the API token that wrote it. Null when there is no one else. |
messages[].data.message.sender_verification | enum ("verified", "not_verified") | yes | Whether Voicegram could confirm who sent this entry. Present on every entry, not only emails. not_verified means the email provider could not confirm the email came from the address it claims; the dashboard shows these as "Sender not confirmed". The entry is still saved and delivered in full. The webhooks guide (https://www.voicegram.io/docs/integrations/webhooks) says how to filter on it before an automation acts on a reply. See "Why couldn't Voicegram confirm who sent an email?" (https://www.voicegram.io/docs/workspace/sender-not-confirmed). |
messages[].data.message.sender_verification_reason | enum ("sender_domain_authenticated", "sender_domain_not_authenticated", "entered_by_signed_in_member", "entered_with_api_token") | yes | Why the entry has its label. sender_domain_authenticated: an email the provider confirmed. sender_domain_not_authenticated: an email it could not confirm (the only not_verified reason). entered_by_signed_in_member: logged by hand in the dashboard. entered_with_api_token: written through this API. |
messages[].data.message.sender_verification_note | string | yes | The same label as one plain-English sentence, derived from sender_verification_reason: "Confirmed by the email provider.", "Entered by a signed-in member.", "Entered with an API token.", or, for not_verified, "The email provider could not confirm this message came from the address it claims. The address may still be genuine, since many small domains have not set up email authentication, but anyone who has this conversation's saving address could also have sent it. Treat it as unconfirmed before automating on it." |
messages[].data.message.recipient_to | array | null | yes | The To addresses of an emailed entry, never including the saving address. Null unless the entry is an email Voicegram received at its saving address (created_via inbound_email), so null on an email logged by hand too, and on an email saved before Voicegram kept them. |
messages[].data.message.recipient_cc | array | null | yes | The Cc addresses of an emailed entry, never including the saving address. Null unless the entry is an email Voicegram received at its saving address (created_via inbound_email), so null on an email logged by hand too, and on an email saved before Voicegram kept them. |
messages[].data.message.subject | string | null | yes | The subject line of an emailed entry. Null unless the entry is an email Voicegram received at its saving address (created_via inbound_email), on an email saved before Voicegram kept it, and on a removed entry. |
messages[].data.message.body | string | yes | The text of the entry. |
messages[].data.message.occurred_at | string (date-time) | yes | When the entry happened. For a logged call or note it is the time given; for an email it is the time of the email. |
messages[].data.message.created_at | string (date-time) | yes | When Voicegram saved the entry. |
messages[].data.message.attachments | array of object | yes | The attachments of an emailed entry, named but never delivered. Empty for anything else. |
messages[].data.message.attachments[].filename | string | yes | |
messages[].data.message.attachments[].content_type | string | null | yes | |
messages[].data.message.attachments[].size_bytes | integer | null | yes | |
messages[].data.sender | object | yes | |
messages[].data.sender.verification_method | enum ("email", "sms", "whatsapp") | yes | The channel the sender used to give their contact information (email, sms or whatsapp). Whether it was confirmed is in contact_verified. The name predates optional verification and is kept so existing integrations keep working. |
messages[].data.sender.contact_verified | boolean | yes | true when the sender proved they own this email address or phone number by entering a one-time code before recording. false when the verification code was turned off for the widget or campaign: the email or phone is exactly what the sender typed and was never confirmed, so it may be mistyped or belong to someone else. |
messages[].data.sender.email | string | null | yes | Populated when verification_method=email, null otherwise. Exactly one of email, phone, and whatsapp is populated. |
messages[].data.sender.phone | string | null | yes | E.164 number, populated when verification_method=sms, null otherwise. Exactly one of email, phone, and whatsapp is populated. |
messages[].data.sender.whatsapp | string | null | yes | E.164 number, populated when verification_method=whatsapp, null otherwise. Exactly one of email, phone, and whatsapp is populated. |
messages[].data.channel | object | yes | |
messages[].data.channel.type | enum ("domain", "campaign") | yes | |
messages[].data.channel.id | string | yes | |
messages[].data.channel.name | string | yes | |
messages[].data.channel.page_url | string | null | yes | Page URL where the Voicegram was recorded. Null for campaign-sourced Voicegrams. |
messages[].data.dashboard_url | string (uri) | yes | Link to the Voicegram in the Voicegram dashboard, opened on its conversation. Requires signing in. |
{
"messages": [
{
"id": "evt_sample-msg-12345678",
"event": "voicegram.message",
"event_id": "evt_sample-msg-12345678",
"delivered_at": "2026-05-13T20:10:05.000Z",
"data": {
"id": "vg_sample_12345678",
"message": {
"id": "msg_sample_12345678",
"medium": "email",
"direction": "inbound",
"source": "logged",
"created_via": "inbound_email",
"author": {
"type": "recorder",
"name": null,
"address": null
},
"logged_by": null,
"sender_verification": "verified",
"sender_verification_reason": "sender_domain_authenticated",
"sender_verification_note": "Confirmed by the email provider.",
"recipient_to": [
"owner@example.com"
],
"recipient_cc": [],
"subject": "Re: Your voice message",
"body": "Thanks for getting back to me. Tuesday at 10 works, see you then.",
"occurred_at": "2026-05-13T20:10:00.000Z",
"created_at": "2026-05-13T20:10:02.000Z",
"attachments": []
},
"sender": {
"verification_method": "email",
"contact_verified": true,
"email": "sample.customer@example.com",
"phone": null,
"whatsapp": null
},
"channel": {
"type": "domain",
"id": "sample-domain-id",
"name": "example.com",
"page_url": "https://example.com/pricing"
},
"dashboard_url": "https://voicegram.io/dashboard/inbox/vg_sample_12345678#conversation"
}
}
]
}
The PAT is missing, malformed, expired, or revoked.
See Errors for the shared error response shape.
PAT is valid but the account is on the free plan.
See Errors for the shared error response shape.
Conversations are not available for the PAT's workspace yet.
See Errors for the shared error response shape.