BuildPulse Auth API

Token validation and caller identity. Send an API token as `Authorization: Bearer bp_` (or `Authorization: token …`). Tokens are created under Organization Settings → API Tokens and shown once; they are bound to one organization and require a plan with API access. Hosted-MCP OAuth sessions use the same endpoints and may span several organizations, selected with `?organization_id=`. This tag also covers the account-scoped reads: the organizations and repositories the caller can see, and the organization's current usage against its plan.

Operations 4

GET /api Validate an API token #
GET /api/me/organizations List organizations accessible to the current token #
GET /api/me/repositories List repositories the current organization can see #
GET /api/v1/usage Current test-result usage for the organization #

Work with this as data

Every API here is available over the APIs.io API and to AI agents over MCP.

MCP server

One button, every client — Claude, Cursor, VS Code and the rest.

https://apis.io/mcp

Tools for apis

7 MCP tools reach this
  • find_apisBrowse and filter every API in the catalog.
  • get_api_artifactsOne API's artifacts, grouped by type.
  • get_openapiThe primary OpenAPI for this API.
  • find_similar_apisAPIs that look like this one.
  • apis_io_searchSTART HERE — APIs, providers and tags for one query, each with its total.
  • resolveTurn a domain, URL or GitHub org into the provider it belongs to.
  • find_cohortsEvery scored population of providers in the catalog.
All 92 tools →

Call it yourself

curl for this page
This API
curl "https://apis.io/api/v1/apis/buildpulse-io:buildpulse-io-auth-api"
All apis
curl "https://apis.io/api/v1/apis?limit=25"

Calls without X-API-Key need no key. Calls with it need a paid plan — get a key.

Get an API key

Free tier, no form to fill in. Signing in shares your email address with us — we store it to create your key and to recognise you if you sign in with another provider. See our Privacy Policy and Terms.

A second provider on the same verified email joins the account you already have.

OpenAPI Specification

buildpulse-io-auth-api-openapi.yml Raw ↑
openapi: 3.2.0
info:
  title: BuildPulse Platform Auth API
  version: 1.0.0
  summary: Public REST API for BuildPulse CI analytics.
  termsOfService: https://buildpulse.io/terms
  description: 'The BuildPulse Platform API exposes read-only test analytics for any

    repository onboarded to BuildPulse: paginated test submissions,

    disruptive/flaky test inventory, per-test disruption history, and SVG

    badges for flakiness and coverage. This API is the drop-in replacement for the legacy Rails API at

    `https://buildpulse.io/api`. Endpoint paths, query parameters, and

    response JSON shapes are intentionally identical so existing

    integrations migrate by changing only the base URL. ## Base URLs


    - Production: `https://platform.buildpulse.io`

    - Development: `https://platform.dev.buildpulse.io`


    ## Authentication


    All endpoints except `/health` require an API token in the

    `Authorization` header. Both `token ` and `Bearer `

    prefixes are accepted. Two token shapes are accepted:


    - **New** (`bp_`) — minted by the BuildPulse web app under

    Organization Settings → API Tokens. SHA256-hashed (lowercase hex)

    and stored in `apiTokens` as

    `{ type: "api_token", tokenPrefix, tokenHash, expiresAt }`. Plan-gated: the organization needs an active plan that includes

    API access (Team, Growth, custom, or a paid legacy Marketplace

    plan; the 14-day trial counts). The same gate applies to the

    hosted MCP''s `mcpSessions`. - **Legacy** (``) — Rails-era format, stored as

    `{ hashedToken: base64(SHA256(token)) }`. Gated against

    `marketplacePurchases.unitCount > 0` for parity with the legacy

    API. Production currently holds zero of these; the path is

    retained for any future migrated tokens. The plaintext token is shown only once at creation time. Example values (do not use):


    ```

    Authorization: Bearer bp_14f56e0c2a8b4d1f9e6a3c7b8d2e5f1a0b9c4d7e6f3a2b1c0d9e8f7a6b5c4d3e

    Authorization: token a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0

    ```


    ## Response shapes


    JSON timestamps are ISO 8601 with second precision in UTC

    (e.g. `2026-05-17T12:34:56Z`). List endpoints always return an array

    for the data field, never `null`. Empty results return `[]`. ## Errors


    | Status | Body |

    |--------|------|

    | 401 | empty |

    | 404 | `{"message":"Not Found"}` |

    | 400 | `{"errors":{"after":[""]}}` |

    | 500 | empty |


    The empty-body 401/500 contract matches the legacy Rails behavior

    (`render body: nil`) and is preserved deliberately. ## Pagination


    Two cursor styles are in use, both base64-encoded so consumers should

    treat them as opaque:


    - **Keyset (submissions only)** — cursor is `base64("id:")`. Stable under inserts. - **Offset (tests, flaky tests)** — cursor is `base64("offset:")`. The next-page cursor is returned as `metadata.after`. When there is

    no next page, `metadata.after` is `null`. ## Reporting window


    Time-bounded endpoints use a rolling window ending now. Which window

    depends on the endpoint:


    | Endpoint | Window |

    |---|---|

    | `GET /api/v1/flaky/tests?quarantine=false` | Your plan''s reporting period (default 14 days) |

    | `GET /api/tests/{id}/results` | Your plan''s reporting period (default 14 days) |

    | `GET /api/v1/flaky/tests` (quarantined view, the default) | Not windowed — every quarantined test is returned |

    | `GET /api/repos/{owner}/{name}/tests` | Not windowed — every test with a disruptiveness ratio above 0 |

    | `GET /api/repos/{owner}/{name}/submissions` | Fixed 14 days |

    | `GET /api/v1/flaky/badges` | Fixed 14 days |


    The plan''s reporting period is resolved per organization from its

    active plan, so it follows plan changes automatically; there is no

    request parameter to override it. Organizations without a resolvable

    plan get the 14-day default. Don''t hardcode a window client-side —

    treat the returned rows as the source of truth. ## For AI agents (MCP)


    The BuildPulse MCP server exposes this API as nine read-only tools

    for Claude, ChatGPT, Cursor, VS Code, and any Model Context

    Protocol client. Hosted: `https://mcp.buildpulse.io/mcp`

    (Streamable HTTP, OAuth sign-in with your BuildPulse account). Stdio: `npx -y @buildpulse/mcp` with `BUILDPULSE_TOKEN` set to an

    API token. Setup and tool list:

    https://platform.buildpulse.io/docs/mcp.'
  license:
    name: Proprietary
    url: https://buildpulse.io/terms
  contact:
    name: BuildPulse Support
    email: support@buildpulse.io
    url: https://buildpulse.io
servers:
- url: https://platform.buildpulse.io
  description: Production
- url: https://platform.dev.buildpulse.io
  description: Development
security:
- TokenAuth: []
- BearerAuth: []
tags:
- name: Auth
  description: 'Token validation and caller identity. Send an API token as

    `Authorization: Bearer bp_` (or `Authorization: token …`).

    Tokens are created under Organization Settings → API Tokens and

    shown once; they are bound to one organization and require a

    plan with API access. Hosted-MCP OAuth sessions use the same

    endpoints and may span several organizations, selected with

    `?organization_id=`. This tag also covers the account-scoped

    reads: the organizations and repositories the caller can see,

    and the organization''s current usage against its plan.'
paths:
  /api:
    get:
      operationId: validateToken
      tags:
      - Auth
      summary: Validate an API token
      description: 'Returns `204 No Content` when the supplied token resolves to an

        organization. Use this as a cheap sanity check before issuing

        other calls.


        Accepts both token shapes (`bp_` and legacy ``).

        The legacy 40-hex path additionally requires

        `marketplacePurchases.unitCount > 0`; the new `bp_` path treats

        issuance as the entitlement check and skips the plan gate.'
      responses:
        '204':
          description: Token is valid (and for legacy 40-hex tokens, the organization has an active plan).
        '401':
          $ref: '#/components/responses/Unauthorized'
      x-codeSamples:
      - lang: shell
        label: curl
        source: "curl -i -H \"Authorization: token $BUILDPULSE_TOKEN\" \\\n  https://platform.buildpulse.io/api\n"
      - lang: javascript
        label: Node
        source: "const res = await fetch(\"https://platform.buildpulse.io/api\", {\n  headers: { Authorization: `token ${process.env.BUILDPULSE_TOKEN}` }\n});\nconsole.log(res.status); // 204\n"
      - lang: python
        label: Python
        source: "import os, requests\nr = requests.get(\n  \"https://platform.buildpulse.io/api\",\n  headers={\"Authorization\": f\"token {os.environ['BUILDPULSE_TOKEN']}\"}\n)\nprint(r.status_code)  # 204\n"
  /api/me/organizations:
    get:
      operationId: listMyOrganizations
      tags:
      - Auth
      summary: List organizations accessible to the current token
      description: 'Returns the set of organizations the calling token may scope

        requests to.


        For long-lived **apiToken** callers (single-org by

        construction), the list always has one element — the org the

        token was generated under.


        For ephemeral **mcpSession** callers (OAuth-minted from the

        hosted MCP), the list contains every org the authenticated

        Cognito user has a membership in. The agent should call this

        endpoint to enumerate the user''s orgs, then pass the chosen

        `id` back as `?organization_id=` on subsequent

        org-scoped requests.'
      responses:
        '200':
          description: Caller's accessible organizations.
          content:
            application/json:
              schema:
                type: object
                required:
                - organizations
                properties:
                  organizations:
                    type: array
                    items:
                      type: object
                      required:
                      - id
                      - name
                      properties:
                        id:
                          type: string
                          description: 'Organization UUID (the value of the

                            `id` field on the organization document

                            — NOT the internal `_id` ObjectID).

                            Pass this as `?organization_id=…` on

                            subsequent requests.

                            '
                        name:
                          type: string
                        slug:
                          type: string
              example:
                organizations:
                - id: 544f50de-5a15-4506-bb79-f00fb29e7317
                  name: Acme Inc.
                  slug: acme
                - id: 8e3d2c1b-9a07-4f24-b1e3-3a8c2cdda8e1
                  name: Acme Spinoff
                  slug: acme-spinoff
        '401':
          $ref: '#/components/responses/Unauthorized'
      x-codeSamples:
      - lang: shell
        label: curl
        source: "curl -H \"Authorization: Bearer $BUILDPULSE_TOKEN\" \\\n  https://platform.buildpulse.io/api/me/organizations\n"
  /api/me/repositories:
    get:
      operationId: listMyRepositories
      tags:
      - Auth
      summary: List repositories the current organization can see
      description: 'Returns the repositories scoped to the caller''s current

        organization (resolved by the auth middleware from the token

        and, for multi-org mcpSession callers, the

        `?organization_id=` query parameter).


        Used by the MCP server''s `list_repositories` tool so the

        model can disambiguate "do I have flaky tests?" without the

        user having to remember repo names.'
      responses:
        '200':
          description: Caller's accessible repositories.
          content:
            application/json:
              schema:
                type: object
                required:
                - repositories
                properties:
                  repositories:
                    type: array
                    items:
                      type: object
                      required:
                      - id
                      - name
                      - full_name
                      - url
                      properties:
                        id:
                          type: string
                          description: Repository UUID.
                        name:
                          type: string
                        full_name:
                          type: string
                          description: '`owner/name`.'
                        url:
                          type: string
                        description:
                          type: string
              example:
                repositories:
                - id: 16a071e9-4e58-4f7d-99f2-d1a965fe91da
                  name: web-client
                  full_name: BuildPulseLLC/web-client
                  url: https://github.com/BuildPulseLLC/web-client
                  description: BuildPulse web app.
        '401':
          $ref: '#/components/responses/Unauthorized'
      x-codeSamples:
      - lang: shell
        label: curl
        source: "curl -H \"Authorization: Bearer $BUILDPULSE_TOKEN\" \\\n  https://platform.buildpulse.io/api/me/repositories\n"
  /api/v1/usage:
    get:
      operationId: getUsage
      tags:
      - Auth
      summary: Current test-result usage for the organization
      description: 'Returns the calling organization''s test-result usage for the

        current UTC calendar month, measured against its plan limit.


        This is the read-only entitlement contract consumed by the

        web-client and the MCP server. It is **always open** — it is

        never subject to overage read-enforcement and never returns

        `402`, so a restricted organization can still discover why its

        data reads are gated.


        `status` is the staged-overage stage (the same derivation every

        surface uses — derived from the canonical fields the ingestion

        lambda stamps on the organization):

        - `within` — under the limit, or not on an enforced plan

        (legacy / free / unlimited orgs always report `within`).

        - `notify` — over the limit, days 0–2 (or in trial): full data

        access plus a banner. Not restricted.

        - `soft` — over the limit, days 3–13: data-read endpoints return

        `402 plan_limit_exceeded`. Ingestion is unaffected.

        - `hard` — over the limit, day 14+: data-read endpoints return

        `402` AND ingestion is paused (raw retained ~30 days).


        Only `soft` and `hard` gate reads; `within` and `notify` keep full

        access. `limit` is `0` for organizations with no enforced monthly

        limit.'
      responses:
        '200':
          description: Current usage snapshot.
          content:
            application/json:
              schema:
                type: object
                required:
                - usage
                - limit
                - status
                - periodStart
                properties:
                  usage:
                    type: integer
                    description: Test results recorded this UTC month.
                  limit:
                    type: integer
                    description: Monthly test-result limit (0 = unlimited / not enforced).
                  status:
                    type: string
                    enum:
                    - within
                    - notify
                    - soft
                    - hard
                  periodStart:
                    type: string
                    format: date-time
                    description: Start of the current UTC calendar month (RFC3339).
              example:
                usage: 1240000
                limit: 3000000
                status: within
                periodStart: '2026-06-01T00:00:00Z'
        '401':
          $ref: '#/components/responses/Unauthorized'
      x-codeSamples:
      - lang: shell
        label: curl
        source: "curl -H \"Authorization: Bearer $BUILDPULSE_TOKEN\" \\\n  https://platform.buildpulse.io/api/v1/usage\n"
components:
  responses:
    Unauthorized:
      description: 'Missing, malformed, or invalid token; or — for legacy 40-hex

        tokens only — the organization has no active plan

        (`marketplacePurchases.unitCount > 0`). The new `bp_<64-hex>`

        path has no plan gate, so a 401 there means the token shape

        was wrong, the hash didn''t match, or the token has expired.

        Response body is empty (matches legacy Rails).

        '
  securitySchemes:
    TokenAuth:
      type: apiKey
      in: header
      name: Authorization
      description: "Send `Authorization: token <token>`. Two token shapes are\naccepted:\n\n- **New** (`bp_<64-hex>`) — minted by the refactored web-client\n  today (`web-client/src/app/_services/apiTokens.ts`). Stored as\n  `{ type: \"api_token\", tokenPrefix, tokenHash (hex SHA256),\n  expiresAt }`. No active-plan gate; issuance is the\n  entitlement check.\n- **Legacy** (`<40-hex>`) — Rails-era format, stored as\n  `{ hashedToken: base64(SHA256(token)) }`. Plan-gated against\n  `marketplacePurchases.unitCount > 0`. Retained for any future\n  migrated tokens.\n\nThe legacy API only accepted `token`, so existing integrations\nneed no changes.\n"
    BearerAuth:
      type: http
      scheme: bearer
      bearerFormat: bp_<64-hex> or <40-hex>
      description: 'Send `Authorization: Bearer <token>`. Functionally identical to

        `TokenAuth`; both header forms are accepted. Same two token

        shapes apply — see `TokenAuth` for the format and plan-gate

        details.

        '
externalDocs:
  description: BuildPulse docs, MCP server and guides
  url: https://platform.buildpulse.io/docs/mcp