Pulse REST API · v1

Burnzoft Pulse REST API

Connect Pulse to your other systems. Read your tickets with their QA grades, every failed metric, your components and grading levels, your people and their timesheets, and log time from another tool. Typical uses: QA dashboards in Power BI or Looker, coaching reports, payroll and project-billing exports, and syncing time from other tools.

The API speaks JSON over HTTPS. Every call is scoped to your company and signs in with an API key. The API is part of Pulse Pro.

Base URL: https://pulse.burnzoft.com/api/v1

Quick start

  1. A company admin opens Company Settings → API & integrations and creates a key. Copy it: it's shown only once.
  2. Keep it in an environment variable, never in your code:
bash
export PULSE_API_KEY="pk_live_..."
  1. Make your first call:
bash
curl -H "Authorization: Bearer $PULSE_API_KEY" \
  "https://pulse.burnzoft.com/api/v1/myself.php"

Authentication

Send your API key in the Authorization header of every request:

http
Authorization: Bearer pk_live_3f9a0c1e...
  • Read-only keys can call every GET endpoint. Read/write keys can also log time.
  • A key belongs to the company, not to a person, and keeps working when its creator leaves. Revoke it in Company Settings when you stop using it.
  • Pulse stores only a fingerprint of the key, so a lost key can't be shown again. Create a new one and revoke the old.
  • Treat keys like passwords. Call the API from your server, never from a web page or app your users can open.

Paging, dates and errors

Paging

List endpoints return a page at a time. Ask for the next page with start_at until is_last is true. max_results is up to 100.

json
{ "start_at": 0, "max_results": 50, "total": 148, "is_last": false, "values": [ ... ] }

Dates and times

Dates are YYYY-MM-DD. Times are ISO 8601 with their offset, e.g. 2026-09-30T21:57:08+08:00. Durations are HH:MM, with minutes alongside for sums.

Errors

Errors have an HTTP status, a message for people and a code for your program. Validation errors add fields.

json
{
  "error": "Some fields are not valid.",
  "code": "validation_failed",
  "fields": { "duration": "HH:MM, more than 00:00 and under 24:00." }
}
StatuscodeMeaning
400invalid_parameter / invalid_jsonA query parameter or the body isn't valid. fields says which.
401unauthorizedNo API key, or it's wrong or revoked.
402pro_requiredThe company doesn't have Pulse Pro.
403read_only_key / company_suspendedThe key can't make changes, or the company is suspended.
404not_foundNo such record in your company.
405method_not_allowedThat method isn't supported here.
422validation_failedThe data you sent isn't valid. fields has a message per field.
429rate_limitedOver 120 requests a minute. Wait Retry-After seconds.

Rate limits

Each key can make 120 requests a minute. Every response says where you stand in X-RateLimit-Limit and X-RateLimit-Remaining. Over the limit you get 429 with a Retry-After header in seconds. Fetch 100 at a time and cache what doesn't change often, like components and people.

API reference

All paths are relative to the base URL. Every call needs the Authorization header.

Check your key

GET/myself.php

Returns the company and API key the request signs in as. The quickest way to test a new key.

Example request

bash
curl -H "Authorization: Bearer $PULSE_API_KEY" \
  "https://pulse.burnzoft.com/api/v1/myself.php"

Example response

json
{
  "company": { "id": 12, "name": "Acme", "tracker": "jira" },
  "api_key": { "name": "Power BI", "hint": "pk_live_3f9a...", "access": "read", "created_at": "2026-10-04T09:15:00+08:00" },
  "rate_limit_per_minute": 120
}

List tickets

GET/tickets.php

Tickets in Pulse, newest submitted first, with their QA grade. A ticket's grade is the average of its forms' grades; grade is null until it's graded.

Query parameterDescription
statusin_progress, completed or cancelled. Default: all.
developer_idOnly this developer's tickets (a person id).
submitted_from, submitted_toSubmission date range, YYYY-MM-DD (inclusive).
gradedtrue for graded tickets only, false for not yet graded.
start_at, max_resultsPaging. max_results is up to 100 (default 50).

Example request

bash
curl -H "Authorization: Bearer $PULSE_API_KEY" \
  "https://pulse.burnzoft.com/api/v1/tickets.php?status=completed&submitted_from=2026-09-01&max_results=2"

Example response

json
{
  "start_at": 0,
  "max_results": 2,
  "total": 148,
  "is_last": false,
  "values": [
    {
      "id": 8190,
      "key": "SERV-52580",
      "status": "completed",
      "priority": "normal",
      "component": { "id": 1, "name": "Onboarding eForm" },
      "test_environment": "Test",
      "developer": { "id": 16, "name": "Lena Cruz" },
      "qa": { "id": 3, "name": "Sam Reyes" },
      "submitted_at": "2026-09-30T21:57:08+08:00",
      "due_date": "2026-10-08",
      "qa_completed_at": "2026-10-02T10:12:00+08:00",
      "grade": 96,
      "repeats": 1,
      "revisions": 0,
      "immediate_resolution": false
    }
  ]
}

Get a ticket with its grades

GET/tickets.php?id={id}

One ticket with its forms. Each form has its grade (100 minus the points of its failed metrics) and the failed metrics themselves. Use ?key=SERV-52580 to look it up by ticket key instead.

Query parameterDescription
idThe ticket's Pulse id.
keyOr the ticket key from your tracker, e.g. SERV-52580.

Example request

bash
curl -H "Authorization: Bearer $PULSE_API_KEY" \
  "https://pulse.burnzoft.com/api/v1/tickets.php?key=SERV-52580"

Example response

json
{
  "id": 8190,
  "key": "SERV-52580",
  "status": "completed",
  "grade": 96,
  "...": "(the same fields as in the list)",
  "forms": [
    {
      "id": 11254,
      "name": "New hire form",
      "version": "2",
      "complexity": "Simple",
      "grade": 96,
      "repeats": 1,
      "failed_metrics": [
        {
          "metric": { "id": 1, "name": "Dimension and/or box model" },
          "level": { "id": 4, "name": "Failed-Average", "points": 4 },
          "developer": { "id": 16, "name": "Lena Cruz" },
          "missed": false
        }
      ]
    }
  ]
}

List failed metrics

GET/failed_metrics.php

Every metric QA failed, across tickets: which ticket and form, which metric, at which level and how many points it cost, and whose work it was. Good for trend reports and coaching.

Query parameterDescription
submitted_from, submitted_toThe ticket's submission date range, YYYY-MM-DD.
developer_idOnly this developer's.
ticket_idOnly this ticket's.
component_idOnly tickets of this component.
start_at, max_resultsPaging.

Example request

bash
curl -H "Authorization: Bearer $PULSE_API_KEY" \
  "https://pulse.burnzoft.com/api/v1/failed_metrics.php?submitted_from=2026-09-01&developer_id=16"

Example response

json
{
  "start_at": 0,
  "max_results": 50,
  "total": 7,
  "is_last": true,
  "values": [
    {
      "id": 232277,
      "ticket": { "id": 8187, "key": "SERV-52964", "submitted_at": "2026-09-17T18:19:21+08:00" },
      "form": { "id": 11265, "name": "Benefits form" },
      "component": { "id": 1, "name": "Onboarding eForm" },
      "metric": { "id": 1, "name": "Dimension and/or box model" },
      "level": { "id": 4, "name": "Failed-Average", "points": 4 },
      "developer": { "id": 16, "name": "Lena Cruz" },
      "missed": true
    }
  ]
}

Components, metrics and levels

GET/components.php

What QA grades against: your components, each with its metrics, and the grading levels. A level with points above 0 is a failure; its points are deducted from the form's 100.

Example request

bash
curl -H "Authorization: Bearer $PULSE_API_KEY" \
  "https://pulse.burnzoft.com/api/v1/components.php"

Example response

json
{
  "components": [
    { "id": 1, "name": "Onboarding eForm", "metrics": [ { "id": 1, "name": "Dimension and/or box model" } ] }
  ],
  "levels": [
    { "id": 1, "name": "Passed", "points": 0, "failed": false },
    { "id": 3, "name": "Failed-Minor", "points": 2, "failed": true },
    { "id": 5, "name": "Failed-Major", "points": 10, "failed": true }
  ]
}

List people

GET/people.php

Your company's people with their roles. Use their ids to filter tickets, failed metrics and timesheets, or to log time for them. ?id={id} returns one person.

Query parameterDescription
activetrue (default) for current people, false for people who left.
idOne person.
start_at, max_resultsPaging.

Example request

bash
curl -H "Authorization: Bearer $PULSE_API_KEY" \
  "https://pulse.burnzoft.com/api/v1/people.php"

Example response

json
{
  "start_at": 0,
  "max_results": 50,
  "total": 19,
  "is_last": true,
  "values": [
    { "id": 16, "username": "lena.cruz", "name": "Lena Cruz", "email": "lena@acme.com", "active": true, "roles": ["Developer"] }
  ]
}

List timesheet entries

GET/timesheets.php

Time logged in the Timesheet, newest first. type is ticket (time on a ticket), internal or administrative (time on a category).

Query parameterDescription
from, toRequired. Date range, YYYY-MM-DD, at most 93 days.
user_idOnly this person's time.
start_at, max_resultsPaging.

Example request

bash
curl -H "Authorization: Bearer $PULSE_API_KEY" \
  "https://pulse.burnzoft.com/api/v1/timesheets.php?from=2026-09-01&to=2026-09-30&user_id=16"

Example response

json
{
  "start_at": 0,
  "max_results": 50,
  "total": 42,
  "is_last": true,
  "values": [
    {
      "id": 90311,
      "user": { "id": 16, "name": "Lena Cruz" },
      "date": "2026-09-30",
      "duration": "01:30",
      "minutes": 90,
      "type": "ticket",
      "ticket": { "id": 8190, "key": "SERV-52580" },
      "category": null,
      "description": "Fixed the box model findings"
    }
  ]
}

List timesheet categories

GET/timesheets.php?categories=1

The active categories time can be logged on (projects, meetings, admin work...).

Example request

bash
curl -H "Authorization: Bearer $PULSE_API_KEY" \
  "https://pulse.burnzoft.com/api/v1/timesheets.php?categories=1"

Example response

json
{
  "categories": [
    { "id": 3352, "name": "Client Product Support", "type": "administrative" },
    { "id": 1, "name": "Internal meetings", "type": "internal" }
  ]
}

Log timeread/write key

POST/timesheets.php

Logs time on a category for a person, e.g. from your project or payroll tool. Needs a read/write key. Returns the new entry with 201 Created.

Body fieldDescription
user_idRequired. The person (see List people).
category_idRequired. An active category (see List timesheet categories).
dateRequired. YYYY-MM-DD.
durationRequired. HH:MM, e.g. 01:30.
descriptionRequired. What was done, up to 5000 characters.

Example request

bash
curl -X POST \
  -H "Authorization: Bearer $PULSE_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"user_id": 16, "category_id": 3352, "date": "2026-10-03", "duration": "01:30", "description": "Client call about the new forms"}' \
  "https://pulse.burnzoft.com/api/v1/timesheets.php"

Example response

json
{
  "id": 90412,
  "user": { "id": 16, "name": "Lena Cruz" },
  "date": "2026-10-03",
  "duration": "01:30",
  "minutes": 90,
  "type": "administrative",
  "ticket": null,
  "category": { "id": 3352, "name": "Client Product Support" },
  "description": "Client call about the new forms"
}

Examples

Export last month's graded tickets to CSV

Pages through every graded ticket submitted in September and writes a spreadsheet you can open in Excel or load into Power BI.

python
import os, csv, requests

BASE = "https://pulse.burnzoft.com/api/v1"
HEADERS = {"Authorization": f"Bearer {os.environ['PULSE_API_KEY']}"}

def all_pages(path, **params):
    """Yields every value of a list endpoint, page by page."""
    start = 0
    while True:
        page = requests.get(f"{BASE}{path}", headers=HEADERS,
                            params={**params, "start_at": start, "max_results": 100}).json()
        yield from page["values"]
        if page["is_last"]:
            break
        start += len(page["values"])

with open("graded_tickets.csv", "w", newline="") as f:
    out = csv.writer(f)
    out.writerow(["key", "developer", "grade", "repeats", "submitted"])
    for t in all_pages("/tickets.php", graded="true", submitted_from="2026-09-01", submitted_to="2026-09-30"):
        out.writerow([t["key"], (t["developer"] or {}).get("name"), t["grade"], t["repeats"], t["submitted_at"]])

Counts this month's failed metrics by developer and metric: a starting point for coaching or a weekly QA digest.

javascript
// Node 18+ (built-in fetch). Which metrics fail most this month, per developer?
const BASE = "https://pulse.burnzoft.com/api/v1";
const headers = { Authorization: `Bearer ${process.env.PULSE_API_KEY}` };

const counts = {};
let start = 0, page;
do {
  const url = `${BASE}/failed_metrics.php?submitted_from=2026-10-01&start_at=${start}&max_results=100`;
  page = await (await fetch(url, { headers })).json();
  for (const f of page.values) {
    const who = f.developer?.name ?? "Unknown";
    counts[who] ??= {};
    counts[who][f.metric.name] = (counts[who][f.metric.name] ?? 0) + 1;
  }
  start += page.values.length;
} while (!page.is_last);

console.table(counts);

Handle rate limits

python
# Respect the rate limit: on 429, wait the Retry-After seconds and try again.
resp = requests.get(url, headers=HEADERS)
if resp.status_code == 429:
    time.sleep(int(resp.headers.get("Retry-After", "60")))
    resp = requests.get(url, headers=HEADERS)

Versioning

This is version 1 (/api/v1/). Within v1 we only add things, such as new endpoints, new optional parameters and new fields. We never remove or rename. Ignore fields you don't know. Anything that would break your integration comes as v2, announced well before v1 changes.

Questions or something missing? Ask on the Support page in Pulse.