> ## Documentation Index
> Fetch the complete documentation index at: https://docs.partfinder.dev/llms.txt
> Use this file to discover all available pages before exploring further.

> ## Agent Instructions
> The PartFinder API is served at https://api.partfinder.dev. Authenticate with `Authorization: Bearer $PARTFINDER_API_KEY`; create an account at https://console.partfinder.dev/sign-up and pick pay as you go or a monthly or yearly plan (see https://partfinder.dev/pricing), then create a key. Pay as you go starts with 100 free credits, once per person and per company.
> Install the PartFinder agent skill with `npx skills add https://docs.partfinder.dev` (skill file: https://docs.partfinder.dev/skill.md).
> The OpenAPI document at https://api.partfinder.dev/v1/openapi.json is the source of truth for request and response schemas.
> What each call costs and current plans: https://api.partfinder.dev/llms.txt.

# Webhooks

> Be told when a supplier replies to one of your requests for quote, when one bounces, or when a search or a comparison ends, instead of polling.

Register an address on your server once. Each time a supplier replies to one of your requests for quote,
one cannot be delivered, or one of your [searches](https://docs.partfinder.dev/search.md) or [comparisons](https://docs.partfinder.dev/compare.md) ends, we send that
address a signed event naming the request for quote, the search or the comparison. Read the details with
`GET /v1/rfq/{id}`, `GET /v1/search/{id}` or `GET /v1/compare/{id}`.

## Register an endpoint

```python Python theme={null}
import os

import requests

response = requests.post(
    "https://api.partfinder.dev/v1/webhooks",
    headers={"Authorization": f"Bearer {os.environ['PARTFINDER_API_KEY']}"},
    json={"url": "https://api.example.com/hooks/partfinder"},
)
print(response.json())
```

```javascript JavaScript theme={null}
const response = await fetch("https://api.partfinder.dev/v1/webhooks", {
  method: "POST",
  headers: {
    Authorization: `Bearer ${process.env.PARTFINDER_API_KEY}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({ url: "https://api.example.com/hooks/partfinder" }),
});
console.log(await response.json());
```

```bash cURL theme={null}
curl https://api.partfinder.dev/v1/webhooks \
  -X POST \
  -H "Authorization: Bearer $PARTFINDER_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"url": "https://api.example.com/hooks/partfinder"}'
```

The response carries the endpoint's signing `secret`, the only time it is shown. Store it with your
other secrets. The address must be `https://` and on the public internet.

[`GET /v1/webhooks`](https://docs.partfinder.dev/api-reference/webhooks/list-webhook-endpoints) lists your endpoints, never their
secrets, and [`DELETE /v1/webhooks/{id}`](https://docs.partfinder.dev/api-reference/webhooks/delete-a-webhook-endpoint) stops
sending to one.

## Who receives what

* An endpoint registered with one of your organization's own keys receives every event of your
  organization.
* An endpoint registered with a key you created for one of your customers
  ([Serving your own customers](https://docs.partfinder.dev/authentication.md#serving-your-own-customers)) receives only that
  customer's events. Each event carries the customer's `end_customer_id`.

## Check every event

Every event is signed in the [Standard Webhooks](https://www.standardwebhooks.com) format, with the
headers `webhook-id`, `webhook-timestamp` and `webhook-signature`. Check the signature with your
endpoint's secret before trusting the body; the Standard Webhooks libraries do it in one call:

```python Python theme={null}
import os

from standardwebhooks import Webhook

wh = Webhook(os.environ["PARTFINDER_WEBHOOK_SECRET"])
# raw_body: the request body exactly as received, before any JSON parsing.
event = wh.verify(raw_body, request.headers)
```

```javascript JavaScript theme={null}
import { Webhook } from "standardwebhooks";

const wh = new Webhook(process.env.PARTFINDER_WEBHOOK_SECRET);
// rawBody: the request body exactly as received, before any JSON parsing.
const event = wh.verify(rawBody, request.headers);
```

A call that throws means the event did not come from us, was changed, or is too old: answer `400`
and ignore it.

## Rotating a signing secret

To replace an endpoint's secret without missing an event:

1. Register the same address again with `POST /v1/webhooks`, using the same key as before, so the new
   endpoint receives the same events. Store the new `secret` it returns.
2. Accept an event signed with either secret. While both endpoints exist, each event is sent to both,
   with the same `webhook-id`: ignore the repeat as you would any other.
3. Once your server checks events with the new secret, delete the old endpoint with
   [`DELETE /v1/webhooks/{id}`](https://docs.partfinder.dev/api-reference/webhooks/delete-a-webhook-endpoint). Its ID is in
   [`GET /v1/webhooks`](https://docs.partfinder.dev/api-reference/webhooks/list-webhook-endpoints). Any delivery still being
   retried to the old endpoint stops.

## Answer quickly, and expect repeats

* Answer with any `2xx` within 15 seconds. Do the work afterwards; the event only names the request
  for quote, the search or the comparison.
* Anything else, or no answer, is retried, over about three days.
* An event can arrive more than once. `webhook-id` is the same on every attempt: use it to ignore a
  repeat.

## When deliveries fail

The console's **Webhooks** page shows each delivery's status and your server's **Last answer**. See
[Console](https://docs.partfinder.dev/console.md#webhooks).

| Last answer | What happened | What to do |
| - | - | - |
| No answer | Your server did not answer within 15 seconds, or could not be reached. | Check that it is up and reachable from the public internet over HTTPS. |
| `3xx` | Your server redirected the request. Redirects are not followed. | Register the final address. |
| `4xx` | Your server refused the request. | Check the path, and that it accepts `POST` and our signature. |
| `5xx` | Your server returned an error. | Check its logs. |

The four events and their fields are in the [API reference](https://docs.partfinder.dev/api-reference/overview.md).


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.