API DocumentationBeta

Getting started guide for the sesh REST API and webhooks.

Introduction

The sesh API enables you to programmatically access your Discord server's events, polls, and time finders, and integrate with external systems through webhooks. You can retrieve data, manage webhook subscriptions, and receive real-time notifications when events occur in your server.

API access requires sesh Pro. Visit the dashboard to upgrade your server.
The sesh API is currently in beta. Please report any issues on our Discord.

Getting Started

  1. Ensure your server has sesh Pro enabled
  2. Go to the sesh Dashboard
  3. Navigate to your server's settings
  4. Generate an API token in the API section
  5. Store your token securely - it will only be shown once

Base URL: All API requests should be made to https://sesh.fyi/api/v1/

Authentication

All API requests must include your API token in the Authorization header using the Bearer scheme.

Token Format

API tokens follow this format:

sesh_g{guild_id}_{random_string}

For example: sesh_g123456789012345678_abc123def456...

Example Request

curl -H "Authorization: Bearer sesh_g123456789_abc123..." \
     https://sesh.fyi/api/v1/events

Security: Keep your API token secret. Do not expose it in client-side code, public repositories, or share it with others. If your token is compromised, regenerate it immediately from the dashboard.

API Reference

For a complete list of endpoints, request parameters, and response schemas, see the full API reference.

The examples below provide a condensed starter guide to help you get up and running quickly.

Code Examples

Fetching Events with curl

# List upcoming events
curl -H "Authorization: Bearer $SESH_API_TOKEN" \
     "https://sesh.fyi/api/v1/events?start_after=$(date -u +%Y-%m-%dT%H:%M:%SZ)"

# Get a specific event
curl -H "Authorization: Bearer $SESH_API_TOKEN" \
     "https://sesh.fyi/api/v1/events/1234567890123456789"

# Get RSVPs for an event (paginated)
curl -H "Authorization: Bearer $SESH_API_TOKEN" \
     "https://sesh.fyi/api/v1/events/1234567890123456789/rsvps?limit=50&offset=0"

# Filter RSVPs by option emoji
curl -H "Authorization: Bearer $SESH_API_TOKEN" \
     "https://sesh.fyi/api/v1/events/1234567890123456789/rsvps?option=✅"

Fetching Polls with curl

# List all polls
curl -H "Authorization: Bearer $SESH_API_TOKEN" \
     "https://sesh.fyi/api/v1/polls"

# Get a specific poll
curl -H "Authorization: Bearer $SESH_API_TOKEN" \
     "https://sesh.fyi/api/v1/polls/1234567890123456789"

# Get votes for a poll (paginated)
curl -H "Authorization: Bearer $SESH_API_TOKEN" \
     "https://sesh.fyi/api/v1/polls/1234567890123456789/votes?limit=50&offset=0"

# Filter votes by option index
curl -H "Authorization: Bearer $SESH_API_TOKEN" \
     "https://sesh.fyi/api/v1/polls/1234567890123456789/votes?option=0"

Creating Polls with curl

# Create a basic poll
curl -X POST https://sesh.fyi/api/v1/polls \
  -H "Authorization: Bearer $SESH_API_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "title": "What game should we play?",
    "options": ["Minecraft", "Valorant", "Among Us"],
    "channel_id": "123456789012345678"
  }'

# Create a Time poll (options are ISO 8601 timestamps)
curl -X POST https://sesh.fyi/api/v1/polls \
  -H "Authorization: Bearer $SESH_API_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "title": "When should we meet?",
    "options": ["2024-01-20T15:00:00.000Z", "2024-01-21T18:00:00.000Z"],
    "channel_id": "123456789012345678",
    "poll_type": "Time"
  }'

# Create an anonymous poll (hides voter names)
curl -X POST https://sesh.fyi/api/v1/polls \
  -H "Authorization: Bearer $SESH_API_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "title": "Anonymous feedback poll",
    "options": ["Great", "Good", "Needs improvement"],
    "channel_id": "123456789012345678",
    "is_anonymous": true
  }'

# Create a poll with role restrictions (Premium)
curl -X POST https://sesh.fyi/api/v1/polls \
  -H "Authorization: Bearer $SESH_API_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "title": "Members-only vote",
    "options": ["Option A", "Option B"],
    "channel_id": "123456789012345678",
    "eligible_role_ids": ["111111111111111111", "222222222222222222"]
  }'

# Create a poll with mentions
curl -X POST https://sesh.fyi/api/v1/polls \
  -H "Authorization: Bearer $SESH_API_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "title": "Team vote",
    "options": ["Yes", "No"],
    "channel_id": "123456789012345678",
    "role_mention_ids": ["333333333333333333"],
    "user_mention_ids": ["444444444444444444"]
  }'

Fetching Time Finders with curl

# List all time finders
curl -H "Authorization: Bearer $SESH_API_TOKEN" \
     "https://sesh.fyi/api/v1/timefinders"

# Get a specific time finder
curl -H "Authorization: Bearer $SESH_API_TOKEN" \
     "https://sesh.fyi/api/v1/timefinders/a1b2c3d4-e5f6-7890-abcd-ef1234567890"

Creating a Webhook with curl

curl -X POST https://sesh.fyi/api/v1/webhooks \
  -H "Authorization: Bearer $SESH_API_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://your-server.com/webhooks/sesh",
    "event_types": ["event.created", "event.updated", "event.rsvp.added"],
    "description": "Main webhook"
  }'

Node.js: Verifying Webhook Signatures

const crypto = require('crypto');

function verifyWebhookSignature(payload, signature, secret) {
  // Parse the signature header (format: sha256=...)
  if (!signature.startsWith('sha256=')) {
    return false;
  }
  const providedSig = signature.slice(7); // Remove 'sha256=' prefix

  // Compute expected signature
  const expectedSig = crypto
    .createHmac('sha256', secret)
    .update(payload)
    .digest('hex');

  // Constant-time comparison
  return crypto.timingSafeEqual(
    Buffer.from(providedSig),
    Buffer.from(expectedSig)
  );
}

// Express.js example
app.post('/webhooks/sesh', express.raw({ type: 'application/json' }), (req, res) => {
  const signature = req.headers['x-sesh-signature'];
  const payload = req.body.toString();

  if (!verifyWebhookSignature(payload, signature, process.env.WEBHOOK_SECRET)) {
    return res.status(401).send('Invalid signature');
  }

  const event = JSON.parse(payload);
  console.log('Received webhook:', event.type);

  // Process the webhook asynchronously
  processWebhook(event).catch(console.error);

  // Return 200 immediately
  res.status(200).send('OK');
});
Webhooks

Webhooks allow you to receive real-time HTTP notifications when events occur in your Discord server. Instead of polling the API, your server receives a POST request whenever something happens.

HTTPS Required: Webhook URLs must use HTTPS for security.

Event Types

Subscribe to any combination of these 19 event types:

Event Events
event.created
A new event was created
event.updated
An event's details were modified
event.deleted
An event was deleted
event.rsvp.added
A user RSVP'd to an event
event.rsvp.removed
A user removed their RSVP
event.started
An event's start time was reached
event.ended
An event's end time was reached
Poll Events
poll.created
A new poll was created
poll.updated
A poll's details were modified
poll.deleted
A poll was deleted
poll.option.added
A new option was added to a poll
poll.vote.added
A user voted on a poll option
poll.vote.removed
A user removed their vote
poll.ended
A poll's voting period ended
TimeFinder Events
timefinder.created
A new TimeFinder was created
timefinder.updated
A TimeFinder's details were modified
timefinder.deleted
A TimeFinder was deleted
timefinder.vote.added
A user voted on a time option
timefinder.vote.removed
A user removed their vote

For webhook management endpoints (create, update, delete, view deliveries), see the Webhooks API Reference.

Payload Format

All webhook payloads follow this structure:

{
  "id": "550e8400-e29b-41d4-a716-446655440000",
  "type": "event.created",
  "timestamp": "2024-01-15T19:00:00.000Z",
  "guild_id": "791937460056752128",
  "data": {
    "id": "1234567890123456789",
    "name": "Weekly Game Night",
    "description": "Join us for games!",
    "start_time": "2024-01-20T19:00:00.000Z",
    "end_time": "2024-01-20T22:00:00.000Z",
    "timezone": "America/New_York",
    "channel_id": "123456789012345678",
    "author_id": "987654321098765432",
    "is_recurring": false,
    "recurring_parent_id": null,
    "rsvp_options": [
      { "emoji": "\u2705", "name": "Going", "count": 0 },
      { "emoji": "\u274C", "name": "Not Going", "count": 0 },
      { "emoji": "\u2753", "name": "Maybe", "count": 0 }
    ]
  }
}
Field Description
id Unique identifier for this delivery (useful for deduplication)
type The event type that triggered this webhook
timestamp When the event occurred (ISO 8601 format)
guild_id The Discord guild (server) ID where the event occurred
data The full object related to the event

Signature Verification

Every webhook request includes an X-Sesh-Signature header containing an HMAC-SHA256 signature. You should verify this signature to ensure the request came from sesh.

The signature is computed as:

HMAC-SHA256(webhook_secret, request_body)

The header format is:

X-Sesh-Signature: sha256=5257a869e7ecebeda32affa62cdca3fa51cad7e77a0e56ff536d0ce8e108d8f9

To verify:

  1. Extract the hex signature after sha256= from the header
  2. Compute the HMAC-SHA256 of the raw request body using your webhook secret
  3. Compare your computed signature (as hex) with the header value
  4. Use constant-time comparison to prevent timing attacks

Retry Schedule

If your endpoint returns a non-2xx status code or times out, sesh will retry the delivery with exponential backoff:

Attempt Delay After Failure
1st retry 1 minute
2nd retry 5 minutes
3rd retry 30 minutes
4th retry 2 hours
5th retry 12 hours

After 5 failed retries, the delivery is marked as failed. You can view delivery history via the /webhooks/:id/deliveries endpoint.

Tip: Return a 2xx response as quickly as possible. Process webhook data asynchronously to avoid timeouts.

Rate Limits

To ensure fair usage and protect the API from abuse, rate limits are enforced on all endpoints.

Operation Type Sustained Rate Burst Rate
Read operations (GET) 300 requests/minute 30 requests/second
Write operations (POST, PATCH, DELETE) 60 requests/minute 10 requests/second

Rate Limit Headers

Each response includes headers to help you track your rate limit status:

Header Description
X-RateLimit-Limit Maximum requests allowed in the current window
X-RateLimit-Remaining Requests remaining in the current window
X-RateLimit-Reset Seconds until the rate limit resets

When you exceed the rate limit, the API returns a 429 Too Many Requests response. Wait until the reset time before making additional requests.