Skip to content

API and webhooks

Pipemetry exposes a read-only JSON API for your own workspace’s numbers, plus outbound webhook endpoints you can register and verify.

API keys

An org admin mints keys from workspace settings. Three things to know:

  • The full key is returned exactly once, at creation. Pipemetry stores only its SHA-256 hash plus a short display prefix, so it can never show you the key again — copy it then.
  • Listing keys returns metadata only: prefix, name, timestamps, revoked state.
  • Revoking is immediate and idempotent. A revoked key stops resolving on its very next request.

Keys look like pm_live_…. A token that does not carry that prefix is rejected at the edge without a database lookup.

The read API

Authenticate with the key in a Bearer header:

Terminal window
curl -H "Authorization: Bearer pm_live_…" \
"https://<your-pipemetry-api-host>/v1/forecast?as_of_day=2026-08-07"

There is no development bypass on /v1 — a valid key is required in every environment.

EndpointReturns
GET /v1/forecastThe forecast-category roll-up and totals
GET /v1/opportunitiesReconstructed opportunities, plus a full-cohort win-rate block
GET /v1/accuracySubmitted-vs-actual scorecards (MAPE, bias, compliance)

Money values are decimal strings on the wire, not floats — so nothing is lost to binary floating point before it reaches your spreadsheet or warehouse.

GET /v1/forecast

ParameterNotes
as_of_dayRequired. The calendar day to roll up as of
group_byowner (default), segment, or stage
model_idOptional. Omitted means your workspace’s active model. Pro
forecast_typeall (default), new_business, renewal, expansion. Pro when not all
measurebookings (default) or arr. Pro when arr

The base roll-up — owner/segment/stage, your active model, whole-cohort bookings — is available on Starter. The three parameters marked Pro are each checked only when you actually use them, so a Starter key gets a working forecast and a 403 upgrade_required only if it asks for a Pro capability. An unrecognised model_id is a 422.

GET /v1/opportunities

as_of_day is required. Narrow with stage, owner, segment, forecast_category, deal_type (new_business / renewal / expansion) or q (free text over name and account). Page with limit (1–200, default 50) and offset, and sort with sort + dir. An unsupported sort field is a 422 — the sortable set is curated server-side.

The response also carries win_rate, settled_won and settled_lost, computed over every settled deal the key can see — not just the page you requested.

GET /v1/accuracy

as_of_day is required. periods selects how many closed periods to score (default 4, max 8), and group_by is owner or manager. model_id is accepted here too, and scores the AI line under that model; an unrecognised value is a 422. See Accuracy and backtesting for what the fields mean.

Two capabilities on this endpoint are sold on Progroup_by=manager (manager roll-up) and choosing a model — but neither is enforced on this path today, unlike on /v1/forecast. Build against your plan rather than against what the endpoint currently allows.

Rate limits

Reads are limited to 120 requests per minute by default. Two things to know before you size an integration against that number:

  • The limit is not per API key. It is keyed on the connecting host, which behind Pipemetry’s edge proxy is the proxy itself — so in practice it behaves as a shared ceiling rather than a per-caller allowance. A separate per-IP limit applies at the edge underneath it.
  • /v1 shares the bucket with the app’s own reads. The same read limiter serves the browser-facing endpoints, so heavy API traffic and heavy app usage draw on the same budget.

Every response — allowed or rejected — carries X-RateLimit-Limit and X-RateLimit-Remaining, and a 429 also carries Retry-After: 60. Cache and back off rather than polling hard.

Webhooks

An org admin registers an HTTPS endpoint per workspace.

  • Register returns the signing secret (whsec_…) once. Store it on your receiver; it is never shown again.
  • The URL is validated before it is accepted: HTTPS only, and no loopback or private-network hosts. A rejected URL is a 422 invalid_webhook_url. This is what stops a registered webhook being used to reach internal addresses.
  • Listing returns metadata only — URL, description, active flag, last delivery state — never the secret.
  • Send test dispatches a signed test.ping envelope to every active endpoint and reports each one’s outcome, so you can prove your receiver works end to end.

The envelope

{
"id": "",
"type": "test.ping",
"workspace_id": "",
"created_at": "",
"data": {}
}

Verifying a delivery

Each POST carries a Pipemetry-Signature header in Stripe’s format:

Pipemetry-Signature: t=<unix-timestamp>,v1=<hex-hmac-sha256>

To verify:

  1. Read t and v1 from the header.
  2. Compute HMAC-SHA256(secret, "<t>.<raw request body>") and hex-encode it.
  3. Compare it to v1 using a constant-time comparison.
  4. Reject the delivery if t is more than 300 seconds away from your clock — that is the replay window Pipemetry’s own verifier uses.

Use the raw body bytes as received. Pipemetry serialises the envelope canonically — JSON with sorted keys and no whitespace — so re-serialising a parsed object will reproduce the same bytes, but reading the raw body is the safer habit.

Error responses

StatusMeaning
401Missing Bearer token, or a key that is malformed, unknown or revoked
402 payment_requiredBilling is suspended for the workspace
403 upgrade_requiredThe plan does not grant the capability you asked for
422Invalid input — an unknown model_id, an unsupported sort, a rejected webhook URL
429Rate limited; retry after the Retry-After interval
503The service is not able to serve that read right now