Getting StartedStatic Site GenerationVisual EditorEdit via CLIComponentsWebhooks

Webhooks

Afia's HTTP API tracks various events, such as customer.created, domain.updated and file.deleted.

You can register a webhook endpoint that will get notified whenever certain events occur.

When your endpoint responds with an HTTP status code between 200-299, Afia considers the webhook as delivered. Otherwise, Afia will retry the webhook later.

Afia guarantees at-least-once delivery. Ensure that your webhook endpoints support receiving the same payload multiple times.

Trying Out Webhooks

First, start a local API server:

afia serve --create-key sk_local_123 --api-port 9200

Send a request to the API server to create a new WebhookEndpoint:

curl -H 'Authorization: Bearer sk_local_123' \
  http://localhost:9200/unstable/webhook_endpoints \
  --json '{"url": "http://localhost:5500/my-webhook-handler", "events": ["customer.created"]}'

Whenever you create a Customer, Afia will send an event to localhost:5500/my-webhook-handler.

We will now create an server to handle the webhook events.

Create a my-webhook-handler.rs file with the following contents:

#!/usr/bin/env cargo +nightly -Zscript
---
[dependencies]
axum = "0.8"
serde_json = "1"
tokio = { version = "1", features = ["rt-multi-thread"] }
---
use axum::{extract, http::StatusCode, routing::post, Router};
use tokio::net::TcpListener;

#[tokio::main]
async fn main() {
    let app = Router::new().route("/my-webhook-handler", post(handle_webhook));
    let listener = TcpListener::bind("0.0.0.0:5500").await.unwrap();
    println!("Listening on port 5500");
    axum::serve(listener, app).await.unwrap();
}

async fn handle_webhook(extract::Json(payload): extract::Json<serde_json::Value>) -> StatusCode {
    let payload_pretty = serde_json::to_string_pretty(&payload).unwrap();
    println!(
        r#"Received webhook payload:
{payload_pretty}"#
    );
    StatusCode::OK
}

Start the webhook handler server:

cargo +nightly -Zscript run --manifest-path=./my-webhook-handler.rs

Use the local API server to create a customer:

curl -H 'Authorization: Bearer sk_local_123' http://localhost:9200/unstable/customers --json '{}'

The webhook handler server should print something like:

{
  "created_at": "2030-03-08:01:23.456789Z",
  "data": {
    "object": {
      "customer_id": "cus_E737igTiZP7zP2s2T4aeFhZPZs",
      "email": null,
      "name": null,
      "org_id": "org_yiykiAb2gt7rb3nHcEc2z6ehf6",
      "phone": null
    },
    "type": "customer.created"
  },
  "event_id": "ev_aP3aZGeEktAzEPGEEfgKGaLKTa"
}

Retries

If webhook delivery fails, Afia will periodically retry using exponential backoff.

If the webhook fails to deliver after several days, Afia will stop retrying.

The commands below will trigger a webhook before starting the webhook handler server from above.

The first send will fail since the server is not running. After a few seconds, Afia will retry. The handler will then receive and print the webhook payload.

afia serve --create-key sk_local_123 --api-port 9200

curl -H 'Authorization: Bearer sk_local_123' \
  http://localhost:9200/unstable/webhook_endpoints \
  --json '{"url": "http://localhost:5500/my-webhook-handler", "events": ["customer.created"]}'
  
curl -H 'Authorization: Bearer sk_local_123' http://localhost:9200/unstable/customers --json '{}'

cargo +nightly -Zscript run --manifest-path=./my-webhook-handler.rs

Delivery Order

Webhooks can be delivered out of order. Your handler should not assume that it will receive payloads in any particular order.