Back to Docs

API Reference

Complete reference for the Pulse public API. Manage changelogs, roadmaps, feedback, notifications, and announcements for your projects.

Overview

The Pulse API is organized around REST. All endpoints accept and return JSON. The public API is available at the base URL below. Every request is scoped to a specific project via the :slugpath parameter, which is your project’s unique URL slug.

Base URL

https://pulse.g8n.ai/api/v1/projects/:slug

For example, if your project slug is my-app, all requests will be prefixed with https://pulse.g8n.ai/api/v1/projects/my-app.

Authentication

The public API endpoints documented on this page resolve projects by their URL slug and do not require API key authentication. They are designed to be called from client-side code, such as the embeddable widget.

Admin API (Bearer Token)

The Admin API at /api/v1/admin/* requires an API key. Generate keys from your project settings in the Pulse dashboard. Keys are prefixed with plsk_ and authenticated via SHA-256 hash comparison using timing-safe equality.

Include your API key in the Authorization header:

Admin API authentication
curl -H "Authorization: Bearer plsk_your_api_key_here" \
  https://pulse.g8n.ai/api/v1/admin/changelog

Visitor Identification

Several endpoints require an x-pulse-user-id header to identify the visitor. This is an anonymous UUID generated client-side (similar to an analytics anonymous ID). It is not an authenticated user credential. It must be a valid UUID v4.

Visitor ID header
x-pulse-user-id: 550e8400-e29b-41d4-a716-446655440000

API Key Scopes

Admin API keys include scopes that determine what operations are permitted. The scopes are stored alongside the key and returned during validation. Common scopes include read, write, and module-specific scopes. Expired keys are automatically rejected.

Rate Limiting

All API requests are rate-limited using an in-memory sliding window. Limits are applied per endpoint group and per identifier (project slug or user ID).

CategoryMax RequestsWindowApplies To
General API10060 secondsChangelog, Roadmap, Notifications, Announcements, Votes, Views
Feedback1060 secondsFeedback submission, Feedback messages
Auth560 secondsAuthentication attempts (Admin API)

When the rate limit is exceeded, the API returns a 429 Too Many Requests response:

{
  "data": null,
  "error": "Rate limit exceeded"
}

Response Format

All responses follow a consistent JSON envelope with two fields: data and error. On success, error is null. On failure, data is null.

Success Response

HTTP 200 OK
{
  "data": [ ... ],
  "error": null
}

Error Response

HTTP 4xx / 5xx
{
  "data": null,
  "error": "Human-readable error message"
}

CORS

All responses include permissive CORS headers to allow requests from any origin. The allowed headers are: Content-Type, Authorization, x-api-key, and x-pulse-user-id.

Error Handling

The API uses standard HTTP status codes to indicate success or failure.

Status CodeMeaning
200Success
201Created (resource successfully created)
400Bad Request (invalid or missing parameters)
401Unauthorized (missing or invalid x-pulse-user-id / API key)
403Forbidden (insufficient permissions)
404Not Found (project, entry, or resource does not exist)
429Too Many Requests (rate limit exceeded)
500Internal Server Error

Changelog

Retrieve published changelog entries, check for unseen entries, and mark entries as viewed. Entries are returned in reverse chronological order by publication date.

GET/projects/:slug/changelog

List changelog entries

Returns all published changelog entries for a project, ordered by published_at descending.

Path Parameters

ParameterTypeRequiredDescription
slugstringRequiredProject URL slug

Response (array of entries)

200 OK
{
  "data": [
    {
      "id": "uuid",
      "version": "1.2.0",
      "title": "New Dashboard Layout",
      "summary": "Redesigned the main dashboard...",
      "content": "<p>Full HTML content...</p>",
      "type": "feature",
      "category": "ui",
      "is_highlight": true,
      "ai_summary": "AI-generated summary text",
      "published_at": "2025-03-15T10:00:00Z",
      "created_at": "2025-03-14T08:30:00Z"
    }
  ],
  "error": null
}

Error Responses

404Project not found
429Rate limit exceeded
GET/projects/:slug/changelog/unseen

Get unseen changelog entries

Returns the count and IDs of published changelog entries that the given visitor has not yet viewed. Used by the widget to show a badge count.

Headers

ParameterTypeRequiredDescription
x-pulse-user-idstring (UUID)RequiredAnonymous visitor identifier

Path Parameters

ParameterTypeRequiredDescription
slugstringRequiredProject URL slug

Response

200 OK
{
  "data": {
    "unseen_count": 3,
    "unseen_ids": [
      "uuid-1",
      "uuid-2",
      "uuid-3"
    ]
  },
  "error": null
}

Error Responses

401x-pulse-user-id header required
404Project not found
429Rate limit exceeded
POST/projects/:slug/changelog/:id/viewed

Mark a changelog entry as viewed

Records that a visitor has viewed a specific changelog entry. Uses an upsert, so calling this multiple times for the same visitor and entry is safe (idempotent).

Headers

ParameterTypeRequiredDescription
x-pulse-user-idstring (UUID)RequiredAnonymous visitor identifier

Path Parameters

ParameterTypeRequiredDescription
slugstringRequiredProject URL slug
idstring (UUID)RequiredChangelog entry ID

Response

200 OK
{
  "data": {
    "viewed": true
  },
  "error": null
}

Error Responses

401x-pulse-user-id header required
404Project or entry not found
429Rate limit exceeded

This endpoint is idempotent. Duplicate calls will not create duplicate view records.

Roadmap

Retrieve published roadmap entries and submit votes. Entries are ordered by milestone number (ascending) then sort order (ascending).

GET/projects/:slug/roadmap

List roadmap entries

Returns all published roadmap entries for a project, ordered by milestone number and sort order.

Path Parameters

ParameterTypeRequiredDescription
slugstringRequiredProject URL slug

Response (array of entries)

200 OK
{
  "data": [
    {
      "id": "uuid",
      "milestone_number": 1,
      "milestone_name": "v2.0 Launch",
      "feature_name": "Dark Mode Support",
      "feature_status": "in_progress",
      "user_friendly_description": "Full dark mode theme...",
      "target_date": "2025-06-01",
      "category": "ui",
      "tags": ["theme", "accessibility"],
      "sort_order": 1,
      "is_highlight": false,
      "vote_count": 42,
      "published_at": "2025-03-01T00:00:00Z"
    }
  ],
  "error": null
}

Error Responses

404Project not found
429Rate limit exceeded
POST/projects/:slug/roadmap/:id/vote

Vote on a roadmap entry

Submit an upvote or downvote on a roadmap entry. Voting is toggle-based: sending the same vote type again removes the vote. Sending a different vote type switches it. The vote_count is maintained by a database trigger.

Headers

ParameterTypeRequiredDescription
x-pulse-user-idstring (UUID)RequiredAnonymous visitor identifier (must be valid UUID v4)

Path Parameters

ParameterTypeRequiredDescription
slugstringRequiredProject URL slug
idstring (UUID)RequiredRoadmap entry ID

Request Body

ParameterTypeRequiredDescription
vote_type"up" | "down"RequiredThe type of vote to cast

Response

200 OK
{
  "data": {
    "vote_count": 43,
    "user_vote": "up"
  },
  "error": null
}

Error Responses

400Invalid JSON body or vote_type
400x-pulse-user-id must be a valid UUID
401x-pulse-user-id header required
404Project or entry not found
429Rate limit exceeded

Voting the same type again removes the vote (toggle). user_vote will be null when the vote is removed.

vote_count is updated atomically by a database trigger (update_roadmap_vote_count).

Feedback

Submit user feedback, retrieve feedback history for a user, and add messages to existing feedback threads. Feedback submission uses the stricter feedback rate limit (10 requests per minute).

POST/projects/:slug/feedback

Submit feedback

Create a new feedback entry for a project. Automatically assigns priority based on category (bugs get medium priority, others get low). The status is set to 'new'.

Headers

ParameterTypeRequiredDescription
x-pulse-user-idstring (UUID)OptionalAnonymous visitor identifier. If provided, the feedback is associated with this user.

Path Parameters

ParameterTypeRequiredDescription
slugstringRequiredProject URL slug

Request Body

ParameterTypeRequiredDescription
titlestringRequiredBrief summary of the feedback
descriptionstringOptionalDetailed description
category"bug" | "feature" | "general"OptionalCategory of feedback. Defaults to "general"
emailstringOptionalContact email for follow-up
page_urlstringOptionalURL of the page where feedback was submitted
browser_infoobjectOptionalBrowser metadata (e.g. userAgent, language). Stored as JSON.

Response (201 Created)

200 OK
{
  "data": {
    "id": "uuid"
  },
  "error": null
}

Error Responses

400Invalid JSON body or missing title
404Project not found
429Rate limit exceeded (10/min)

Invalid category values are silently coerced to "general".

Bug reports are automatically assigned "medium" priority; all others get "low".

GET/projects/:slug/feedback

List user feedback

Returns all feedback entries submitted by the specified visitor for a project, ordered by created_at descending.

Headers

ParameterTypeRequiredDescription
x-pulse-user-idstring (UUID)RequiredAnonymous visitor identifier

Path Parameters

ParameterTypeRequiredDescription
slugstringRequiredProject URL slug

Response (array of feedback entries)

200 OK
{
  "data": [
    {
      "id": "uuid",
      "title": "Button not working on mobile",
      "category": "bug",
      "status": "new",
      "priority": "medium",
      "created_at": "2025-03-15T10:00:00Z"
    }
  ],
  "error": null
}

Error Responses

401x-pulse-user-id header required
404Project not found
POST/projects/:slug/feedback/:id/messages

Add a message to a feedback thread

Post a follow-up message on an existing feedback entry. Only the original feedback author (identified by x-pulse-user-id) can add messages to their thread.

Headers

ParameterTypeRequiredDescription
x-pulse-user-idstring (UUID)RequiredMust match the user_id of the original feedback entry

Path Parameters

ParameterTypeRequiredDescription
slugstringRequiredProject URL slug
idstring (UUID)RequiredFeedback entry ID

Request Body

ParameterTypeRequiredDescription
messagestringRequiredThe message text

Response (201 Created)

200 OK
{
  "data": {
    "id": "uuid",
    "feedback_id": "uuid",
    "message": "Here is additional context...",
    "sender_role": "user",
    "sender_id": "visitor-uuid",
    "is_public": true,
    "created_at": "2025-03-15T11:00:00Z"
  },
  "error": null
}

Error Responses

400Invalid JSON body or missing message
401x-pulse-user-id header required
403Forbidden (user is not the feedback author)
404Project or feedback entry not found
429Rate limit exceeded (10/min)

Messages are created with sender_role "user" and is_public true.

Only the original author (matched by x-pulse-user-id) can post messages.

Notifications

Retrieve notifications for a visitor and mark them as read. Returns up to 50 notifications per request, ordered by creation date descending.

GET/projects/:slug/notifications

List notifications

Returns up to 50 notifications for the identified visitor, along with an unread count. Ordered by created_at descending.

Headers

ParameterTypeRequiredDescription
x-pulse-user-idstring (UUID)RequiredAnonymous visitor identifier (must be valid UUID v4)

Path Parameters

ParameterTypeRequiredDescription
slugstringRequiredProject URL slug

Response

200 OK
{
  "data": {
    "notifications": [
      {
        "id": "uuid",
        "type": "changelog",
        "title": "New version released",
        "message": "Version 2.1 is now available",
        "link": "/changelog/v2.1",
        "metadata": {},
        "read_at": null,
        "created_at": "2025-03-15T10:00:00Z"
      }
    ],
    "unreadCount": 1
  },
  "error": null
}

Error Responses

400x-pulse-user-id must be a valid UUID
401x-pulse-user-id header required
404Project not found
429Rate limit exceeded
POST/projects/:slug/notifications/read

Mark notifications as read

Mark a single notification or all unread notifications as read. Send notification_id in the body to mark a specific one, or send an empty body to mark all as read.

Headers

ParameterTypeRequiredDescription
x-pulse-user-idstring (UUID)RequiredAnonymous visitor identifier (must be valid UUID v4)

Path Parameters

ParameterTypeRequiredDescription
slugstringRequiredProject URL slug

Request Body

ParameterTypeRequiredDescription
notification_idstring (UUID)OptionalID of a specific notification to mark as read. Omit to mark all as read.

Response

200 OK
{
  "data": {
    "read": true
  },
  "error": null
}

Error Responses

400x-pulse-user-id must be a valid UUID
401x-pulse-user-id header required
404Project not found
429Rate limit exceeded

An empty body (or invalid JSON) marks all unread notifications as read.

Already-read notifications are not affected.

Announcements

Retrieve active announcements and dismiss them. Announcements support time-based scheduling (starts_at / ends_at) and are automatically filtered to show only currently active entries, ordered by priority descending.

GET/projects/:slug/announcements

List active announcements

Returns all currently active announcements for a project. Automatically filters by time window (starts_at/ends_at) and active status. If x-pulse-user-id is provided, previously dismissed announcements are excluded.

Headers

ParameterTypeRequiredDescription
x-pulse-user-idstring (UUID)OptionalIf provided, dismissed announcements are filtered out

Path Parameters

ParameterTypeRequiredDescription
slugstringRequiredProject URL slug

Response (array of active announcements)

200 OK
{
  "data": [
    {
      "id": "uuid",
      "title": "Scheduled Maintenance",
      "message": "We will be performing maintenance on March 20th.",
      "type": "warning",
      "banner_color": "#d97706",
      "is_dismissible": true,
      "priority": 10,
      "starts_at": "2025-03-18T00:00:00Z",
      "ends_at": "2025-03-21T00:00:00Z"
    }
  ],
  "error": null
}

Error Responses

404Project not found
429Rate limit exceeded

Announcements with type values: "info", "warning", "success", "error".

Only announcements where is_active is true and the current time is within the starts_at/ends_at window (if set) are returned.

POST/projects/:slug/announcements/:id/dismiss

Dismiss an announcement

Records that a visitor has dismissed a specific announcement. The announcement will no longer appear for this visitor in subsequent GET requests. Only announcements with is_dismissible set to true can be dismissed.

Headers

ParameterTypeRequiredDescription
x-pulse-user-idstring (UUID)RequiredAnonymous visitor identifier

Path Parameters

ParameterTypeRequiredDescription
slugstringRequiredProject URL slug
idstring (UUID)RequiredAnnouncement ID

Response

200 OK
{
  "data": {
    "dismissed": true
  },
  "error": null
}

Error Responses

400This announcement cannot be dismissed (is_dismissible is false)
401x-pulse-user-id header required
404Project or announcement not found
429Rate limit exceeded

This endpoint is idempotent. Duplicate dismiss calls are safely handled via upsert.

Embeddable Widget

Pulse provides a drop-in JavaScript widget that adds a feedback button, changelog panel, and announcement banners to any website. The widget renders inside a Shadow DOM to avoid CSS conflicts with your site.

Installation

Add the following script tag to your HTML. Replace your-project-slugwith your project’s slug.

HTML
<script
  src="https://pulse.g8n.ai/api/widget/widget.js"
  data-pulse-project="your-project-slug"
  data-pulse-user="optional-uuid-for-visitor"
  data-pulse-theme="light"
  data-pulse-position="bottom-right"
  data-pulse-features="feedback,changelog,announcements"
  defer
></script>

Configuration Attributes

AttributeRequiredDefaultDescription
data-pulse-projectYes--Your project's URL slug
data-pulse-userNonullUUID to identify the visitor (enables unseen counts, voting, dismissals)
data-pulse-apiNoAuto-detectedOverride the API base URL
data-pulse-themeNo"light""light" or "dark"
data-pulse-positionNo"bottom-right""bottom-right", "bottom-left", "top-right", "top-left"
data-pulse-featuresNo"feedback,changelog,announcements"Comma-separated list of enabled features

How the Widget Uses the API

On initialization, the widget makes the following API calls based on enabled features:

GET/projects/:slug/changelog-- Fetches latest 10 entries
GET/projects/:slug/changelog/unseen-- Badge count (if user ID set)
GET/projects/:slug/announcements-- Active banners
POST/projects/:slug/feedback-- On form submit
POST/projects/:slug/changelog/:id/viewed-- When entry is displayed
POST/projects/:slug/announcements/:id/dismiss-- When user closes banner

Visitor ID Pattern

The data-pulse-user attribute (sent as the x-pulse-user-id header) serves as an anonymous visitor identifier. It is analogous to an analytics anonymous ID and is not an authenticated user credential.

If you have authenticated users, you can pass a stable identifier (or a UUID derived from their user ID) to enable per-user features like unseen changelog counts, vote tracking, and announcement dismissals. If you do not provide a visitor ID, the widget will still work for features that do not require it (viewing changelogs, viewing announcements, submitting feedback).

Generating a visitor ID (JavaScript)
// Generate and persist a visitor ID
function getVisitorId() {
  let id = localStorage.getItem('pulse-visitor-id');
  if (!id) {
    id = crypto.randomUUID();
    localStorage.setItem('pulse-visitor-id', id);
  }
  return id;
}

Need Help?

If you run into issues or have questions about the API, reach out to our support team at support@g8n.ai.