Skip to content
BotLunarBotLunar
Connect Discord

Version 1

The BotLunar API

Read your members and their consent, create and schedule campaigns, read results and analytics, manage your suppression list, and send events from your platform. Every request names one workspace through its key, and nothing outside that workspace can be reached.

Keys, scopes, limits and errors

Create a key in the dashboard under Integrations, API keys, and send it as Authorization: Bearer bl_live_.... It is shown once and only a fingerprint is kept. A key reaches one workspace and nothing else, holds only the scopes chosen for it, and works only while the person who made it is still in the workspace with a role allowed to do what it asks.

Each key may make 600 requests a minute. Every answer carries X-RateLimit-Limit, X-RateLimit-Remaining and X-RateLimit-Reset; past the limit the answer is 429 with Retry-After. Every error has the same shape, with a request id to quote to us:

{ "error": { "code": "missing_scope", "message": "This key does not have the members:read scope.", "request_id": "req_..." } }

Scopes: analytics:read, members:read, members:email (emails are never returned without it), campaigns:manage, suppression:manage and events:send. Discord tells nobody when a message is read, so there is no open rate anywhere in the API.

GET/v1/members/v1/members

List members

Members of the workspace's server with their consent, newest first, a page at a time.

Scope: members:read

Parameters
ParameterInNotes
limitqueryoptional
cursorqueryoptional. next_cursor from the previous page.
opted_inqueryoptional

Example request

curl -X GET "https://api.botlunar.com/v1/members" \
  -H "Authorization: Bearer bl_live_..."

Example answer

{
  "data": [
    {
      "id": "cm1abc",
      "discord_id": "123456789012345678",
      "username": "trader",
      "consent": {
        "opted_in": true,
        "basis": "MEMBER_CONFIRMED",
        "opted_in_at": "2026-10-01T09:00:00Z",
        "unsubscribed_at": null,
        "email_opted_in": false
      }
    }
  ],
  "next_cursor": null
}
  • 200 A page of members.
  • 400 The request was not understood. The error says which field.
  • 401 No key, or a key that is wrong or revoked.
  • 403 The key does not have the scope this needs.
  • 404 Nothing with that id in this workspace.
  • 429 Too many requests for this key. Wait for the number of seconds in Retry-After.

GET/v1/members/{id}/v1/members/{id}

Get one member

Scope: members:read

Parameters
ParameterInNotes
idpathrequired

Example request

curl -X GET "https://api.botlunar.com/v1/members/ID" \
  -H "Authorization: Bearer bl_live_..."
  • 200 The member.
  • 400 The request was not understood. The error says which field.
  • 401 No key, or a key that is wrong or revoked.
  • 403 The key does not have the scope this needs.
  • 404 Nothing with that id in this workspace.
  • 429 Too many requests for this key. Wait for the number of seconds in Retry-After.

GET/v1/campaigns/v1/campaigns

List campaigns

Scope: campaigns:manage

Example request

curl -X GET "https://api.botlunar.com/v1/campaigns" \
  -H "Authorization: Bearer bl_live_..."
  • 200 Campaigns, newest first.
  • 400 The request was not understood. The error says which field.
  • 401 No key, or a key that is wrong or revoked.
  • 403 The key does not have the scope this needs.
  • 404 Nothing with that id in this workspace.
  • 429 Too many requests for this key. Wait for the number of seconds in Retry-After.

POST/v1/campaigns/v1/campaigns

Create a campaign

Creates a draft. Nothing is sent until it is scheduled, and scheduling runs every review check the dashboard runs.

Scope: campaigns:manage

Example request

curl -X POST "https://api.botlunar.com/v1/campaigns" \
  -H "Authorization: Bearer bl_live_..." \
  -H "Content-Type: application/json" \
  -d '{"name":"October funded challenge","body":"Our new challenge opens {countdown}. Trading involves risk.","kind":"offer","audience":{"type":"all_opted_in"}}'
  • 201 The draft.
  • 400 The request was not understood. The error says which field.
  • 401 No key, or a key that is wrong or revoked.
  • 403 The key does not have the scope this needs.
  • 404 Nothing with that id in this workspace.
  • 429 Too many requests for this key. Wait for the number of seconds in Retry-After.

GET/v1/campaigns/{id}/v1/campaigns/{id}

Get a campaign's status

Scope: campaigns:manage

Parameters
ParameterInNotes
idpathrequired

Example request

curl -X GET "https://api.botlunar.com/v1/campaigns/ID" \
  -H "Authorization: Bearer bl_live_..."
  • 200 The campaign.
  • 400 The request was not understood. The error says which field.
  • 401 No key, or a key that is wrong or revoked.
  • 403 The key does not have the scope this needs.
  • 404 Nothing with that id in this workspace.
  • 429 Too many requests for this key. Wait for the number of seconds in Retry-After.

POST/v1/campaigns/{id}/schedule/v1/campaigns/{id}/schedule

Schedule a campaign

A wall clock time in the workspace's own time zone. Every review check runs now and again when it is due. Refused with the review's reason.

Scope: campaigns:manage

Parameters
ParameterInNotes
idpathrequired

Example request

curl -X POST "https://api.botlunar.com/v1/campaigns/ID/schedule" \
  -H "Authorization: Bearer bl_live_..."
  • 200 The scheduled campaign.
  • 400 The request was not understood. The error says which field.
  • 401 No key, or a key that is wrong or revoked.
  • 403 The key does not have the scope this needs.
  • 404 Nothing with that id in this workspace.
  • 429 Too many requests for this key. Wait for the number of seconds in Retry-After.

GET/v1/campaigns/{id}/results/v1/campaigns/{id}/results

Get a campaign's results

Scope: analytics:read

Parameters
ParameterInNotes
idpathrequired

Example request

curl -X GET "https://api.botlunar.com/v1/campaigns/ID/results" \
  -H "Authorization: Bearer bl_live_..."
  • 200 Counted from the campaign's own messages, test sends left out.
  • 400 The request was not understood. The error says which field.
  • 401 No key, or a key that is wrong or revoked.
  • 403 The key does not have the scope this needs.
  • 404 Nothing with that id in this workspace.
  • 429 Too many requests for this key. Wait for the number of seconds in Retry-After.

GET/v1/analytics/v1/analytics

Get analytics for a range of days

Scope: analytics:read

Parameters
ParameterInNotes
fromqueryrequired
toqueryrequired

Example request

curl -X GET "https://api.botlunar.com/v1/analytics" \
  -H "Authorization: Bearer bl_live_..."
  • 200 Totals for the workspace's own calendar days, up to a year.
  • 400 The request was not understood. The error says which field.
  • 401 No key, or a key that is wrong or revoked.
  • 403 The key does not have the scope this needs.
  • 404 Nothing with that id in this workspace.
  • 429 Too many requests for this key. Wait for the number of seconds in Retry-After.

GET/v1/suppression/v1/suppression

List the suppression list

Scope: suppression:manage

Example request

curl -X GET "https://api.botlunar.com/v1/suppression" \
  -H "Authorization: Bearer bl_live_..."
  • 200 Entries, newest first.
  • 400 The request was not understood. The error says which field.
  • 401 No key, or a key that is wrong or revoked.
  • 403 The key does not have the scope this needs.
  • 404 Nothing with that id in this workspace.
  • 429 Too many requests for this key. Wait for the number of seconds in Retry-After.

POST/v1/suppression/v1/suppression

Add to the suppression list

Discord ids, usernames or emails. A listed member is never messaged, whatever the audience.

Scope: suppression:manage

Example request

curl -X POST "https://api.botlunar.com/v1/suppression" \
  -H "Authorization: Bearer bl_live_..." \
  -H "Content-Type: application/json" \
  -d '{"entries":["123456789012345678","someone@example.com"]}'
  • 200 What was added.
  • 400 The request was not understood. The error says which field.
  • 401 No key, or a key that is wrong or revoked.
  • 403 The key does not have the scope this needs.
  • 404 Nothing with that id in this workspace.
  • 429 Too many requests for this key. Wait for the number of seconds in Retry-After.

DELETE/v1/suppression/{id}/v1/suppression/{id}

Take an entry off the suppression list

The entry is marked removed and kept.

Scope: suppression:manage

Parameters
ParameterInNotes
idpathrequired

Example request

curl -X DELETE "https://api.botlunar.com/v1/suppression/ID" \
  -H "Authorization: Bearer bl_live_..."
  • 200 Taken off.
  • 400 The request was not understood. The error says which field.
  • 401 No key, or a key that is wrong or revoked.
  • 403 The key does not have the scope this needs.
  • 404 Nothing with that id in this workspace.
  • 429 Too many requests for this key. Wait for the number of seconds in Retry-After.

POST/v1/events/v1/events

Send an event

The same events as the signed event address, sent with a key instead of a signature. Acted on by the workspace's rules and logged.

Scope: events:send

Example request

curl -X POST "https://api.botlunar.com/v1/events" \
  -H "Authorization: Bearer bl_live_..." \
  -H "Content-Type: application/json" \
  -d '{"id":"evt_123","type":"trader.funded","member":{"discord_id":"123456789012345678"}}'
  • 200 Received, with the result of each rule.
  • 400 The request was not understood. The error says which field.
  • 401 No key, or a key that is wrong or revoked.
  • 403 The key does not have the scope this needs.
  • 404 Nothing with that id in this workspace.
  • 409 This event id was already received.
  • 429 Too many requests for this key. Wait for the number of seconds in Retry-After.