Skip to content

Search RFP.co

Pages across the product, solutions, opportunities and resources.

Type to search. Press Enter to open the full results page.

Start free trial
Developers

Webhooks today, and where the REST API stands

Reference documentation for the RFP.co API: authentication, resources, pagination, rate limits and the webhook events the platform emits.

Signed outbound webhooks are available now and documented in full below — the header, the algorithm and a verifier you can copy. The REST API is a different matter: it is included in three plans and it is not built, which this page says plainly rather than leaving you to discover after subscribing.

What exists, and what does not

Surface, Status, What you can do with it
SurfaceStatusWhat you can do with it
Outbound webhooksAvailableReceive signed events at your own endpoint, verified with the recipe below
Slack deliveryAvailableThe same events into a channel, without writing a receiver
IntegrationsAvailableCRM, document storage and single sign-on connections configured in the workspace
REST APINot builtNothing yet — keys cannot be issued on any plan

Do not build against the application’s own endpoints

The routes the web application calls authenticate with a session cookie, are shaped for its screens, and are not a supported integration surface. They will change without notice and without a version.

The commercial machinery behind the REST API is real — the entitlement keys, the usage metering, the tenant scoping and the token hashing all exist and are tested. What is missing is the public surface itself: the credential, the request pipeline and the routes.

That gap is stated here rather than left quiet because a plan listing an allowance for something that does not exist is a promise. The honest options were to stop selling it or to document where it stands, and this is the second.

The events a webhook can carry

An endpoint subscribes to categories rather than to individual events, and the same delivery preferences that route a notification to email or Slack route it to a webhook. Some events are inherently per-workspace and some are per-pursuit; the audience rules decide who receives one, and a webhook receives what its subscribed categories produce.

Category, Events, Fires when
CategoryEventsFires when
New work foundOpportunity matched · Score threshold crossedA published solicitation scores against one of your profiles
Signals and predictionsBuyer activity · Strong signal · Prediction publishedEvidence of demand appears before anything is solicited
Changes to your pursuitsOpportunity amendedA buyer amends a solicitation you are tracking
DeadlinesOpportunity deadline · Submission deadlineA closing date or an internal submission date approaches
Work assigned to youTask assigned · Review requested · Approval requestedA section changes hands inside the workspace
Documents and deliveryExport readyA generated document or package finishes
BillingAllowance threshold · Payment failedA plan allowance nears its limit, or a charge is declined

Delivery is per-recipient and respects each person’s preferences and digest schedule. A webhook endpoint is a workspace-level destination rather than a person, so it receives events as they happen rather than batched into a digest.

Verifying a delivery

A webhook endpoint is a URL on the public internet that accepts POSTs and acts on them, and URLs leak — through a log, a proxy, a screenshot in a support ticket. Every delivery is therefore signed, and an unverified body should be discarded rather than trusted.

The scheme is Stripe’s, deliberately and without modification. Not because it is clever, but because most backend developers have already written the five lines that check it, and inventing a scheme here would cost you an afternoon and gain nothing.

The header
RFP-Signature: t=1786550400,v1=<hex>

signature = HMAC-SHA256(secret, `${t}.${rawBody}`)
  1. Capture the raw bytes before anything parses them

    Parsing and re-serializing produces different bytes — key order, number formatting, unicode escapes — and every one of those differences fails the check. This is the single most common reason a verifier that looks correct rejects everything.

  2. Parse the header into a timestamp and one or more signatures

    More than one v1 value can be present during a secret rotation. Treat the header as a list, and accept the delivery if any signature matches any of your active secrets.

  3. Compute the expected signature and compare in constant time

    HMAC-SHA256 over the timestamp, a full stop, and the raw body, keyed with the endpoint secret. Compare with a timing-safe comparison rather than string equality.

  4. Check the signature before the timestamp

    A body whose signature does not match is unsigned, not stale, and the order matters: telling somebody their clock is wrong is the wrong answer to give a person who is being attacked.

  5. Reject anything outside the tolerance window

    Five minutes is the suggested window, and it is your choice rather than ours. Without it a captured body can be replayed indefinitely, because a signature alone never expires.

  6. Deduplicate on the event id

    Retries are real and a delivery can arrive twice. The event id in the body is the key to deduplicate on if you would rather do that than expire.

Verifying, in Node
import { createHmac, timingSafeEqual } from 'node:crypto'

const TOLERANCE_SECONDS = 300

export const verify = (header, rawBody, secrets) => {
  const parts = Object.fromEntries(
    header.split(',').map((pair) => pair.split('=', 2)),
  )
  const timestamp = Number(parts.t)
  if (!Number.isFinite(timestamp)) return false

  const provided = header
    .split(',')
    .filter((pair) => pair.startsWith('v1='))
    .map((pair) => pair.slice(3))

  const matched = secrets.some((secret) => {
    const expected = createHmac('sha256', secret)
      .update(`${timestamp}.${rawBody}`)
      .digest('hex')

    return provided.some((signature) => {
      const a = Buffer.from(signature, 'utf8')
      const b = Buffer.from(expected, 'utf8')
      return a.length === b.length && timingSafeEqual(a, b)
    })
  })

  // Signature first: a body that does not match is unsigned, not stale.
  if (!matched) return false

  return Math.abs(Date.now() / 1000 - timestamp) <= TOLERANCE_SECONDS
}

Secrets, rotation and failure

  • A secret is shown once

    It is generated from a cryptographic source, prefixed so that a secret found in a log is identifiable as one, and never rendered again. An interface that can redisplay a secret is backed by a store holding something it should not.

  • Rotation overlaps deliberately

    During a rotation two secrets are valid at once and deliveries carry a signature for each. That is why the header must be read as a list — a verifier that takes the first v1 value works until the day somebody rotates.

  • Retries back off

    A delivery that fails is retried with exponential backoff. Return a 2xx as soon as you have durably accepted the body, and do the work afterwards — a receiver that processes before responding will be retried while it is still working.

  • One endpoint being down does not affect another

    Deliveries are per-endpoint. A failing receiver accumulates its own retries rather than holding up anybody else’s events.

  • The endpoint must be reachable on the public internet

    Private and loopback addresses are rejected when an endpoint is saved, rather than accepted and then failing silently at the first delivery.

The REST API: what is already decided

These decisions are settled, because each is expensive or impossible to change after the first integration depends on it. They are published now so that anything you design in the meantime can be designed against them. The endpoint list is not published, because it is not built.

  • Versioned in the path

    Under /api/v1/. A version in a header is a version callers forget to send; a version in the path is visible in every log line and every bug report.

  • Bearer keys, issued per workspace

    Sent as an Authorization header. Only a keyed fingerprint is stored, keys carry a visible prefix so a leaked one is identifiable without being usable, and revocation is read per request rather than cached — so it takes effect on the next call.

  • Cursor pagination, never offset

    New opportunities arrive constantly and an offset page shifts underneath a caller walking it, silently skipping records. The failure is invisible until somebody reconciles counts.

  • One envelope

    Every response carries data, meta and links. A list and a single record differ in what is inside data, not in the shape around it, and every response carries a request id.

  • Idempotent writes

    Every mutating request accepts an idempotency key and replays the original response for a repeat within twenty-four hours. A response project created twice by a network retry is a real cost.

  • Rate limits you can read

    Limit, remaining and reset on every response; a retry-after on a refusal. A per-minute limit and an exhausted monthly allowance are different answers, because the first is retryable and the second is a plan boundary.

  • Absence and denial look alike across a tenant boundary

    A record belonging to another workspace returns a 404 rather than a 403, because a 403 confirms the record exists.

Plan, API access, Monthly calls, Rate limit
PlanAPI accessMonthly callsRate limit
StarterNot included
ProfessionalIncluded25,00060 per minute
BusinessIncluded150,000300 per minute
AgencyIncluded500,000600 per minute

These are the values held in the billing catalog, which is what the pricing page reads. They are the allowances that will apply when the surface opens; they are not enforceable against anything today, and no allowance accrues in the meantime.

While you wait

Available now

Integrations

CRM, document storage and single sign-on connections, configured in the workspace without a key.

Available now

Delivery and exports

A finished response leaves as a branded link or a PDF, which covers most of what an integration is asked to do with a document.

Genuinely useful

Tell us what you would call

Which resources, in which direction, and what your system is the record for. The read surface is specified; what lands first is not.

Reference

Plan allowances

Every limit, read from the billing catalog rather than restated — including the API allowances above.

Common questions

Can I use the REST API today?

No. It is listed on three plans and the public surface is not built. The endpoints the web application calls are session-authenticated, shaped for its own screens, and will change without notice.

Are webhooks available now?

Yes. An endpoint can be added in workspace settings, subscribed to notification categories, and every delivery is signed with the scheme documented on this page. Slack is available as a destination for the same events.

Why does my signature check fail even though the secret is right?

Almost always because the body was parsed before it was verified. Re-serializing JSON changes key order, number formatting and unicode escapes, and the signature is computed over the bytes as sent. Capture the raw body first.

What happens to deliveries during a secret rotation?

Two secrets are valid at once and each delivery carries a signature for both, so nothing is dropped while you change over. Read the header as a list of signatures rather than taking the first one.

Why is API access sold on plans that cannot use it?

Because the entitlements, the metering and the allowances were built before the surface was. The choice was to stop listing it or to document it as forthcoming, and this page is that documentation.

Will the decided parts of the API change before it ships?

The parts stated here — path versioning, bearer keys, cursor pagination, the envelope, idempotency and the tenant-boundary behaviour — are settled, because each is expensive to retrofit. The endpoint list is unpublished because it is unbuilt, not because it is secret.

When will it be available?

There is no published date. An estimate on a page like this becomes a commitment somebody plans a quarter around, so if an integration is on your critical path, ask us directly and describe it — specified integrations are what order the work.

Related

Integrations

CRM, storage, identity and the API everything else uses.

Pricing

Four plans, read from the billing catalog rather than typed here.

Help center

How the product works, task by task.

Security

How customer data is isolated, encrypted and retained.

See what you are not bidding on.

Connect a source, describe what your company does, and look at the opportunities that come back before deciding whether any of this is worth your time.