Skip to content

Create an endpoint

The endpoint is the address in your system that receives the events. Registering it is a single step, in the dashboard or through the API. This guide covers both paths and ends by checking that deliveries are arriving.

  • Who registers. Webhooks are managed by the owner and admins, in the dashboard or with an API key of a member with that role. Other roles get 403 on every webhook route, including the read ones.
  • A public https:// address. Internal addresses, localhost and http:// are refused, and the check runs again on every delivery. The full rules are in Where a delivery can go. To test from your own machine, you need to expose your local server at a public https:// address, for example with a tunneling service.
  • A server that checks the signature. Have the receiver ready first, with the code from Verify the signature. It answers 200 to a signed delivery and 400 to anything else.
  • The events you use. Pick from the Event catalog only what your system will handle. Receiving an event means receiving its data.

How many endpoints the company can have depends on the plan: see Limits.

  1. Open the Settings page: click your name at the top of the screen, then Settings. From the side menu, the path is Account → Settings.
  2. Go to the API tab. The Webhooks card sits below the keys.
  3. Click New endpoint.
  4. Fill in the URL, a Description to recognize later which system receives the events, and check the Events. All events also includes the ones added to the catalog later.
  5. Confirm with your password and click Create endpoint.

The next screen shows the secret, which starts with whsec_. Copy it to the server that receives the events. In the dashboard, the owner and admins can reveal the secret again later, from the endpoint menu (Reveal secret), confirming the password.

The dashboard asks for the password when creating and whenever the URL or the events change, because those two fields decide where the company’s data goes.

The key needs:

PermissionWhat for
webhooks:createRegister the endpoint
<module>:read for each eventSubscribe: sales:read for sale.*, clients:read for client.*, and so on. With ["*"], for every module
webhooks:updateSend the ping, rotate the secret, turn it on and off. Changing the URL or the events, turning it back on, sending the ping and rotating the secret also need <module>:read for every event the endpoint subscribes to, even if it was created in the dashboard
webhooks:readRead the endpoint and the delivery log
Terminal window
# 1. Register
# Generate the key once and keep it: on a retry, repeat with the same value.
CREATE_KEY=$(uuidgen)
curl -X POST "https://api.fatureihoje.com/public/v1/webhook_endpoints" \
-H "Authorization: Bearer $FH_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $CREATE_KEY" \
-d '{
"url": "https://erp.yourcompany.com/webhooks/faturei-hoje",
"description": "Store ERP",
"events": ["sale.created", "sale.paid", "client.created"]
}'
# 2. Check the connection, with the id that came back above
ENDPOINT_ID="7f3a1c5e-9b2d-4f6a-8c0e-2b4d6f8a0c1e"
# Generate the key once and keep it: on a retry, repeat with the same value.
PING_KEY=$(uuidgen)
curl -X POST "https://api.fatureihoje.com/public/v1/webhook_endpoints/$ENDPOINT_ID/ping" \
-H "Authorization: Bearer $FH_API_KEY" \
-H "Idempotency-Key: $PING_KEY"
# 3. A few seconds later, see how the delivery went
curl "https://api.fatureihoje.com/public/v1/webhook_endpoints/$ENDPOINT_ID/deliveries?limit=5" \
-H "Authorization: Bearer $FH_API_KEY"

The registration response is 201 and carries the endpoint with the secret field. Through the API, the secret only shows up in this response and on rotation. Repeating the registration with the same Idempotency-Key returns the endpoint without the secret, because it is not stored in the clear, not even for the replay. If you lost the response, rotate the secret with POST /public/v1/webhook_endpoints/{id}/rotate_secret, or reveal it in the dashboard.

The owner and admins get an email for every endpoint created.

The ping (POST /public/v1/webhook_endpoints/{id}/ping, or Send ping in the endpoint menu in the dashboard) sends a real ping event to that endpoint only, signed and down the same path as any event. It carries no company data. It is not a test environment: it only proves the address receives and checks the signature.

Then open the delivery log (GET /public/v1/webhook_endpoints/{id}/deliveries, or View deliveries in the dashboard):

What you seeWhat it means
succeededYour server answered 2xx. Done
pending, with response_status_codeThe server answered, but not 2xx. Another attempt comes later
pending, with errorThere was no answer (timeout, DNS, TLS, connection). The reason is in the field
failedThe attempts ran out, or the delivery was closed without being sent. See Deliveries and retries

A 400 on the ping is almost always a signature that did not match on your side: the wrong secret, or the body parsed before the check. See Verify the signature.

ResponseWhy
422 with param urlThe URL is not https://, is not public, has a username and password, or does not resolve
422 with param eventsAn event that is not in the catalog, or * together with another name
403 permission_missingMissing webhooks:create, or the <module>:read for one of the chosen events
403 plan_limit_reachedThe company reached the plan’s endpoint limit
403 member_permission_deniedThe member behind the key is neither the owner nor an admin