Interactive previewDemo data only. Sign in as any example account and try anything. Resets nightly.Launching soon
All resources
For projects

Integrations and API

Send Casefile events to Slack, GitHub, Jira or your own endpoint, and read your programs' records through the REST API.

You manage integrations at Integrations & API in the account menu. Owners and admins with two-factor authentication can create, test, pause and delete them. Everyone else on the team can see the list and recent deliveries.

What leaves Proofline, and what doesn't

Integrations notify you that something happened, not what the vulnerability is. Each delivery carries:

  • the Casefile code, status, severity and a link back to the case
  • the program slug and name
  • the event type and when it happened

Report bodies, proofs of concept, evidence, messages and decision reasoning never leave Proofline. Report titles are off by default because a title can describe an unfixed issue. Turn on Include report titles only for destinations with restricted access, such as a private channel or a private repository.

Delivery guarantees

  • Never blocks a decision. Deliveries are written to an outbox in the same database transaction as the case event and sent by the scheduled job. If Slack is down, triage still works.
  • At-least-once. Failed deliveries retry with exponential backoff (1, 2, 4, 8… minutes, capped at 6 hours) up to 8 attempts, then show as failed on the Integrations page. Use the delivery id to de-duplicate.
  • In order per Casefile for trackers. GitHub and Jira deliveries for the same Casefile are sent in order, so a retry never opens a second issue.
  • SSRF-safe. Endpoints must use HTTPS. Private, loopback, link-local and cloud-metadata addresses are refused when you save the integration and again when each connection opens, which defeats DNS rebinding. Redirects aren't followed, and each request times out after 10 seconds.

Use Send test to deliver a PING immediately. Pause holds deliveries without losing them. They're sent in order when you resume, and anything still undelivered after 7 days expires. Delete removes the integration and its delivery history.

Events

Event Default
REPORT_SUBMITTED: new report on
RESEARCHER_RESPONDED: researcher answered an info request on
REPORT_ACCEPTED on
SEVERITY_CHANGED off
CLOSURE_PROPOSED off
REBUTTAL_SUBMITTED: final rebuttal received, 48h clock running on
MEDIATION_REQUESTED / MEDIATION_DECISION on
RETEST_PASSED / RETEST_FAILED on
REMEDIATION_DECLINED: Won't Fix recorded (still a payout obligation) off
PAYOUT_SENT on
MESSAGE_POSTED: new message in the case room off

Slack

Create an incoming webhook for the channel and paste its https://hooks.slack.com/services/… URL. Each event posts one line with an Open Casefile button.

GitHub Issues

Use a private repository and a fine-grained personal access token scoped to that repository only, with Issues: read & write permission. Enter the repository as owner/repo. The first event for a Casefile opens an issue labelled security and proofline, and later events are added as comments. The issue tracks workflow only and links back to the case for details.

Jira Cloud

Enter your site (https://acme.atlassian.net), credentials as email:api-token (create a token at id.atlassian.com), and the project key. Add :IssueType to choose an issue type, for example SEC:Bug. The default is Task. As with GitHub, there's one ticket per Casefile, and later events become comments.

Signed webhooks

A generic webhook receives a POST with a JSON body:

{
  "id": "cm3x…",
  "type": "REPORT_ACCEPTED",
  "occurredAt": "2026-09-30T18:04:11.000Z",
  "casefile": { "code": "CF-1004", "status": "ACCEPTED", "severity": "HIGH", "url": "https://…/cases/CF-1004" },
  "program": { "slug": "helix", "name": "Helix Protocol" }
}

Each request also carries these headers:

  • Proofline-Event: the event type
  • Proofline-Delivery: the delivery id (stable across retries)
  • Proofline-Signature: t=<unix seconds>,v1=<hex HMAC-SHA256>

The signing secret (whsec_…) is shown once when you create the webhook. Proofline keeps only an encrypted copy. To verify a delivery, compute the HMAC over "<t>.<raw body>", compare it in constant time, and reject old timestamps:

import { createHmac, timingSafeEqual } from "node:crypto";

export function verifyProofline(secret, header, rawBody, toleranceSec = 300) {
  const parts = Object.fromEntries(header.split(",").map((kv) => kv.split("=")));
  const t = Number(parts.t);
  if (!Number.isFinite(t) || Math.abs(Date.now() / 1000 - t) > toleranceSec) return false;
  const expected = createHmac("sha256", secret).update(`${t}.${rawBody}`).digest();
  const given = Buffer.from(parts.v1 ?? "", "hex");
  return given.length === expected.length && timingSafeEqual(given, expected);
}

Verify against the raw request body, before any JSON parsing. Respond with any 2xx within 10 seconds. Anything else is retried.

REST API

The API is read-only and scoped to your organization. Create a key under API keys. It's shown once and stored only as a hash. Send it as a bearer token:

curl -H "Authorization: Bearer pl_live_…" https://<your-proofline-host>/api/v1/casefiles?status=PAYOUT_PENDING

GET /api/v1/casefiles

Lists Casefiles across your programs, newest first.

Query Meaning
status Filter by status, e.g. TRIAGE, ACCEPTED, PAYOUT_PENDING
program Filter by program slug
limit 1–200 (default 50)

Each item has code, program, status, severity, requestedSeverity, outcome, remediation, disclosure, submittedAt, updatedAt, proofline (events, head hash) and url.

GET /api/v1/casefiles/{code}

Returns one Casefile with:

  • the rulebook it's judged under (version and fingerprint)
  • the content hash and lifecycle timestamps
  • every recorded decision (kind, verdict, severity, cited rule, and whether it was rebutted)
  • the current reward and payouts, including on-chain transaction hashes

Codes that belong to other organizations return 404, the same as codes that don't exist.

Errors and limits

Status Meaning
401 unauthorized Missing, invalid or revoked key
404 not_found No such Casefile in your organization
429 rate_limited More than 120 requests per minute per organization. Retry after 60 seconds.

Responses are never cached (Cache-Control: no-store). You can compare proofline.head with the hash shown on the case page to confirm you're looking at the same record.