> For clean Markdown of any page, append .md to the page URL. > For a complete documentation index, see https://docs.givechariot.com/v2026-04-01/api/webhooks/llms.txt. > For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://docs.givechariot.com/_mcp/server. # Webhooks and Events When something interesting happens on the nonprofit's Chariot account, such as a Grant being created or its status being updated, Chariot can send a message to your application so that you can take action automatically. The first step is to create an Event Subscription via the [API](/api/event-subscriptions/create). As part of this, you specify a URL for a server endpoint that will receive real-time events. Chariot will then send HTTPS requests to that URL to notify you of activity for certain events. When a noteworthy event happens, Chariot first generates an [Event](/api/events/get). Next, we'll send a POST request to your endpoint. The body of the `POST` webhook request will be the same as the API representation of the [Event](/api/events/get) resource. **`Webhook Event Payload`** ```json Webhook Event Payload { "id": "event_123abc", "created_at": "2023-01-31T23:59:59Z", "category": "grant.created", "associated_object_type": "grant", "associated_object_id": "67d66b89-51a0-4f17-a7b3-18c5dbac5361" } ``` ### Event Types These events represent specific occurrences within our system that you can subscribe to and receive notifications for. Event names follow the pattern `{resource}.{event_type}` where `event_type` is either `created` or `updated`. Status transitions (for example, a Donor Account moving to `approved` or `rejected`) are delivered as `{resource}.updated` events — fetch the object to read its current status. * `grant.created`: This event is triggered when a grant is created. * `grant.updated`: This event is triggered when a grant is updated. This can happen when the status changes for example. * `unintegrated_grant.created`: This event is triggered when an unintegrated grant is created. * `unintegrated_grant.updated`: This event is triggered when an unintegrated grant is updated. * `donor_account.created`: This event is triggered when a [Donor Account](/api/donor-accounts) is created. * `donor_account.updated`: This event is triggered when a Donor Account is updated. This can happen when the status transitions to `approved`, `rejected`, or `disabled`. * `authorization_token.created`: This event is triggered when an [Authorization Token](/api/authorization-tokens) is created. * `authorization_token.updated`: This event is triggered when an Authorization Token is updated. This can happen when the status transitions to `verified`, `revoked`, or `expired`. * `grant_request.created`: This event is triggered when a [Grant Request](/api/grant-requests) is created. * `grant_request.updated`: This event is triggered when a Grant Request is updated. This can happen when the status transitions to `pending`, `submitted`, or `rejected`. * `disbursement.created`: This event is triggered when a disbursement is created. * `disbursement.updated`: This event is triggered when a disbursement is updated. This can happen when the lifecycle status of the disbursement changes. * `inbound_transfer.created`: This event is triggered when an inbound transfer is created. * `inbound_transfer.updated`: This event is triggered when an inbound transfer is updated. This can happen when the status of the inbound transfer changes. * `donation.created`: This event is triggered when a donation is created. * `donation.updated`: This event is triggered when a donation is updated. * `deposit.created`: This event is triggered when a deposit is created. * `deposit.updated`: This event is triggered when a deposit is updated. This can happen when the status of the deposit or underlying transfer changes. * `verification_request.created`: This event is triggered when a verification request is created. * `verification_request.updated`: This event is triggered when a verification request is updated. Note that you can create Event Subscriptions in Production and Sandbox. Sandbox Event Subscriptions will receive events for Sandbox events and vice versa. ## Consuming Events Individual Events don’t contain very much information on their own. This is by design, as the API structure can remain extremely stable and avoid difficult webhook migrations in the future as the Chariot API changes. If you need additional metadata, such as the amount or status of the Grant in the above example, make a GET request to the API for that information. If you don't want to use webhooks, or need to do some batch processing, you can also request Events from the Chariot API using the [List Events API](/api/events/list). Events will remain in the Chariot system for up to 30 days. ## Secure your webhooks After you've confirmed that your webhook endpoint connections works as expected, secure the connection by verifying the signature provided in the header of the webhook request. > **Note** > > In order to verify signatures for webhooks, you need to specify the signing secret that you will use in the [Create Event Subscription API](/api/event-subscriptions/create). Chariot will include the `Chariot-Webhook-Signature` header in each webhook request. This header will contain a timestamp and one or more signatures. The timestamp is prefixed by `t=`, and each signature is prefixed by a scheme. Schemes start with `v`, followed by an integer. Currently, the only valid live signature scheme is `v1`. ``` Chariot-Webhook-Signature: t=2024-01-19T18:48:56Z,v1=e0632fb61f7d1068ecbde75410d5c3cc152926f97eaacf53c6228624647329da ``` Chariot generates signatures using a hash-based message authentication code ([HMAC](https://en.wikipedia.org/wiki/HMAC)) with [SHA-256](https://en.wikipedia.org/wiki/SHA-2). To prevent [downgrade attacks](https://en.wikipedia.org/wiki/Downgrade_attack), ignore all schemes that are not v1. ### Verify Signatures Manually #### Step 1: Extract the timestamp and signatures from the header Split the header using the `,` character as the separator to get a list of elements. Then split each element using the `=` character as the separator to get a prefix and value pair. The value for the prefix `t` corresponds to the timestamp, and `v1` corresponds to the signature. You can discard all other elements. #### Step 2: Prepare the signed payload The `signed_payload` string is created by concatenating: * The timestamp (as a string) * The character `.` * The actual JSON payload (that is, the request body) #### Step 3: Determine the expected signature Compute an HMAC with the SHA256 hash function. Use the Event Subscription's `signing_secret` as the key, and use the `signed_payload` string as the message. #### Step 4: Compare the Signatures Compare the signature (or signatures) in the header to the expected signature. For an equality match, compute the difference between the current timestamp and the received timestamp, then decide if the difference is within your tolerance. To protect against timing attacks, use a constant-time-string comparison to compare the expected signature to each of the received signatures. ## Failures and Retries In production, if your application returns anything other than a 20x HTTP status code, we’ll retry it up to 10 times with exponentially increasing backoffs. In your webhook endpoint implementation, we recommend you place inbound Events into your application’s own queuing system for asynchronous event processing, and return a 200 response from your endpoint as quickly as possible. Because we can't guarantee we'll receive your 200 acknowledgement, your webhook implementation should gracefully handle receiving the same webhook multiple times. To avoid queueing issues, we will not retry failed webhooks in the sandbox. > **Info** > > If all attempts to a webhook endpoint fail, after 5 days the endpoint will be disabled and we will stop sending event messages to that endpoint. ## Best Practices Review these best practices to make sure your webhooks remain secure and function well with your integration. ### Handle Duplicate Events Webhook endpoints might occasionally receive the same event more than once. You can guard against duplicated event receipts by making your event processing idempotent. ### Only subscribe to event categories your integration requires Configure your webhook endpoints to receive only the types of events required by your integration. Listening for extra events (or all events) puts undue strain on your server and we don’t recommend it. You can [Update an Event Subscription](/api/event-subscriptions/update) to change the event category for a webhook endpoint with the API. ### Receive events with an HTTPS server If you use an HTTPS URL for your webhook endpoint, Chariot validates that the connection to your server is secure before sending your webhook data. For this to work, your server must be correctly configured to support HTTPS with a valid server certificate. The Production environment requires HTTPS URLs. ### Quickly return a 2xx response Your endpoint must quickly return a successful status code (2xx) prior to any complex logic that could cause a timeout. ### Verify webhook signatures See the section above on [verifying webhook signatures](/api/webhooks#secure-your-webhooks) > Listen for events on your Chariot account so your platform can automatically trigger reactions.