Skip to content

Fetch broadcast logs ​

Get ​
bash
https://api.bunce.so/v1/broadcast/logs

Returns a company's delivery and engagement logs — one row per event that happened to a message on its way to a recipient (sent, delivered, opened, clicked, bounced, failed, complained, unsubscribed) — with the originating broadcast and the recipient inlined on each log. Events are available for the last 3 months only.

Requests are automatically scoped to your own company, so you only ever see your own logs.

Request ​

Query Parameters ​

All parameters are optional and combine with AND.

statuses array<enum>Filter by event type. Repeatable: statuses[]=sent&statuses[]=opened.
channel enumFilter by message channel.
broadcast_id string<uuid>Return only logs for one specific broadcast.
customer_ids array<string>Return only logs for the given recipients. Repeatable: customer_ids[]=...&customer_ids[]=....
from dateInclusive lower bound on the event date. Must fall within the last 3 months.
to dateInclusive upper bound on the event date. Must be on or after from.
per_page intPage size, 1..100. If not specified we use a default value of 20.
cursor stringSpecify exactly what cursor you want to retrieve.

Status values

sent, delivered, opened, clicked, bounced, failed, complained, unsubscribed

Channel values

email, whatsapp, sms, push_notification

Date range ​

Logs are available for the last 3 months only — events older than that are not returned.

  • If you omit both from and to, you get the last 3 months of activity. Supplying either bound narrows the range to your own dates.
  • from and to must fall within the last 3 months; dates outside it are rejected.
  • If you supply to earlier than from, the request fails validation (422).
bash
curl --request GET \
  --url 'https://api.bunce.so/v1/broadcast/logs' \
  --header 'Accept: application/json' \
  --header 'X-Authorization: sk_live_************************'
js
import  from 'node-fetch'

let  = 'https://api.bunce.so/v1/broadcast/logs'

let  = {
  : 'GET',
  : {
    : 'application/json',
    'X-Authorization': 'sk_live_************************',
  },
}

(, )
  .then(() => .json())
  .then(() => .())
  .catch(() => .('error:' + ))
go
import (
	"fmt"
	"net/http"
	"io/ioutil"
)

url := "https://api.bunce.so/v1/broadcast/logs"

	req, _ := http.NewRequest("GET", url, nil)

	req.Header.Add("Accept", "application/json")
	req.Header.Add("X-Authorization", "sk_live_************************")

	res, _ := http.DefaultClient.Do(req)

	defer res.Body.Close()
	body, _ := ioutil.ReadAll(res.Body)

	fmt.Println(res)
	fmt.Println(string(body))

Response ​

Logs are returned newest-first (action_at descending).

id stringUnique id of the log entry.
status enumEvent type — one of the Status values.
action_at datetimeWhen the event occurred (ISO-8601).
response string|nullExtra detail attached to the event, when available. For clicked logs this is the URL the recipient clicked.
broadcast object|nullThe broadcast that produced this event. null if it can no longer be resolved.
customer object|nullThe recipient of the message. null if it can no longer be resolved.

broadcast

id stringBroadcast id.
channel enumChannel the broadcast was sent on.
subject string|nullSubject line (email).
message_type enumOne of broadcast or transactional.
created_at datetimeWhen the broadcast was created.

customer

customer_id stringThe recipient's external Bunce customer identifier.
email string|nullRecipient email.
phone_no string|nullRecipient phone number.
first_name string|nullRecipient first name.
last_name string|nullRecipient last name.

meta

has_next_page booleanWhether more results exist after this page.
has_prev_page booleanWhether results exist before this page.
next_page_cursor string|nullPass as cursor to fetch the next page.
prev_page_cursor string|nullPass as cursor to fetch the previous page.
per_page intPage size actually used.
json
{
  "success": true,
  "data": [
    {
      "id": "01J2XK9F3T8QW2E6R1Y5M7C4P0",
      "status": "sent",
      "action_at": "2026-10-05T09:31:05+00:00",
      "response": "Message queued",
      "broadcast": {
        "id": "3f1b2a7e-8c9d-4e2a-9b1f-0a7c5d6e8f90",
        "channel": "email",
        "subject": "Your October newsletter",
        "message_type": "broadcast",
        "created_at": "2026-10-05T09:30:00+00:00"
      },
      "customer": {
        "customer_id": "CUS-EXTERNAL-ID",
        "email": "ada@example.com",
        "phone_no": "+2348012345678",
        "first_name": "Ada",
        "last_name": "Lovelace"
      }
    }
  ],
  "meta": {
    "has_next_page": false,
    "has_prev_page": false,
    "next_page_cursor": null,
    "prev_page_cursor": null,
    "per_page": 20
  },
  "message": "Timeline logs fetched successfully"
}
json
{
  "success": false,
  "message": "X-Authorization is required in headers"
}
json
{
  "success": false,
  "message": "The selected statuses.0 is invalid.",
  "errors": {
    "statuses.0": ["The selected statuses.0 is invalid."]
  }
}

WARNING

broadcast and customer are returned as null — never omitted — when the underlying record can no longer be resolved, so always null-check before reading nested fields.

Examples ​

Messages SENT to a customer within a timeframe ​

Returns the sent events for a given recipient between from and to.

bash
curl --request GET \
  --url 'https://api.bunce.so/v1/broadcast/logs?statuses[]=sent&customer_ids[]=CUS-EXTERNAL-ID&from=2026-10-01&to=2026-10-10' \
  --header 'Accept: application/json' \
  --header 'X-Authorization: sk_live_************************'

Messages OPENED by a customer ​

Each item represents one open event, so a recipient who opened the same message several times appears multiple times (newest first).

bash
curl --request GET \
  --url 'https://api.bunce.so/v1/broadcast/logs?statuses[]=opened&customer_ids[]=CUS-EXTERNAL-ID&from=2026-10-01&to=2026-10-10' \
  --header 'Accept: application/json' \
  --header 'X-Authorization: sk_live_************************'

Messages CLICKED by a customer ​

For clicked logs, response holds the link the recipient clicked.

bash
curl --request GET \
  --url 'https://api.bunce.so/v1/broadcast/logs?statuses[]=clicked&customer_ids[]=CUS-EXTERNAL-ID' \
  --header 'Accept: application/json' \
  --header 'X-Authorization: sk_live_************************'
json
{
  "success": true,
  "data": [
    {
      "id": "01J2XQ7D2N5V8B1M3Z6T9K4W7H",
      "status": "clicked",
      "action_at": "2026-10-05T14:05:12+00:00",
      "response": "https://example.com/offer",
      "broadcast": {
        "id": "3f1b2a7e-8c9d-4e2a-9b1f-0a7c5d6e8f90",
        "channel": "email",
        "subject": "Your October newsletter",
        "message_type": "broadcast",
        "created_at": "2026-10-05T09:30:00+00:00"
      },
      "customer": {
        "customer_id": "CUS-EXTERNAL-ID",
        "email": "ada@example.com",
        "phone_no": "+2348012345678",
        "first_name": "Ada",
        "last_name": "Lovelace"
      }
    }
  ],
  "meta": {
    "has_next_page": false,
    "has_prev_page": false,
    "next_page_cursor": null,
    "prev_page_cursor": null,
    "per_page": 20
  },
  "message": "Timeline logs fetched successfully"
}

Delivery status across a single broadcast ​

bash
curl --request GET \
  --url 'https://api.bunce.so/v1/broadcast/logs?broadcast_id=3f1b2a7e-8c9d-4e2a-9b1f-0a7c5d6e8f90&channel=email' \
  --header 'Accept: application/json' \
  --header 'X-Authorization: sk_live_************************'

Paging with the cursor ​

bash
curl --request GET \
  --url 'https://api.bunce.so/v1/broadcast/logs?statuses[]=delivered&per_page=2' \
  --header 'Accept: application/json' \
  --header 'X-Authorization: sk_live_************************'

Then follow up with the returned meta.next_page_cursor, keeping your filters:

bash
curl --request GET \
  --url 'https://api.bunce.so/v1/broadcast/logs?statuses[]=delivered&per_page=2&cursor=eyJhY3Rpb25fYXQi...' \
  --header 'Accept: application/json' \
  --header 'X-Authorization: sk_live_************************'

When no logs match the filters, data is an empty array and the request is still 200 OK.