Skip to content

API and webhooks

Put tender data where your team already works

A versioned REST API for matched opportunities, readiness and bid status, and signed webhooks when something changes. Available on Pro and Enterprise plans.

Keys that belong to your organisation

  • Keys are owned by the organisation, not a person, so they survive staff changes.
  • Each key has scopes such as opportunities:read or bids:read.
  • Shown once, stored hashed, revocable instantly, with per-key rate limits.
  • Every call appears in your organisation's activity log.
Request
curl https://app.example.com/api/public/v1/opportunities?closingWithinDays=14 \
  -H "Authorization: Bearer tnd_live_…"

The OpenAPI 3.1 reference is generated from the same schemas the API validates with, and lists each endpoint as it goes live: openapi.json

Webhooks you can trust

Every delivery is signed with HMAC-SHA256 and a timestamp, carries a stable event ID for de-duplication, and is retried over 24 hours if your endpoint is down.

Verify a delivery (Node.js)
import { createHmac, timingSafeEqual } from "node:crypto";

// header: Tender-Signature: t=1759140000,v1=5f2c…
export function verify(rawBody: string, header: string, secret: string) {
  const { t, v1 } = Object.fromEntries(header.split(",").map((p) => p.split("=")));
  if (Math.abs(Date.now() / 1000 - Number(t)) > 300) return false; // replay window
  const expected = createHmac("sha256", secret).update(`${t}.${rawBody}`).digest();
  const given = Buffer.from(v1, "hex");
  return given.length === expected.length && timingSafeEqual(given, expected);
}

Events

Webhook events
EventSent when
opportunity.matchedA new tender matches your organisation
tender.changedA tender you follow changed materially: deadline, value, documents, clarifications
tender.deadline_approachingA followed tender closes within your chosen window
readiness.changedYour readiness score or blockers changed
bid.status_changedA bid moved state, for example to approved or submitted
evidence.expiringA document a bid depends on is about to expire