Webhook Endpoints

Webhook endpoints in Obelisk provide a way to trigger executions via incoming HTTP requests. They act as entry points into your Obelisk system, allowing external services and applications to interact with your workflows and activities.

Key Concepts

Creating a Webhook Endpoint

  1. Definition: Define a Rust function that will handle incoming HTTP requests. If the function is annotated with the #[handler] macro from the waki crate, the macro handles the necessary WASI 0.2 HTTP imports and exports.

  2. WIT Imports: Similar to workflows, in order to call workflows or activities, you need to import their WIT interfaces.

  3. Compilation to WASM: The Rust code is compiled into a WebAssembly Component.

  4. Deployment: Create a [[webhook_endpoint]] table in your obelisk.toml Obelisk configuration file. This table specifies:

    • name: A unique name for the endpoint.
    • http_server: An association to a http_server
    • location: A table that defines where to load the wasm component from. It can be either location.path to a local file or location.oci to use a OCI registry.
    • routes: A request matching any of the specified routes will be forwarded to this endpoint. See next sections for details.
  5. Execution: When an HTTP request is made to a matching route, Obelisk:

    • Loads the associated WebAssembly Component.
    • Invokes the handler function, passing in the request and path parameters.
    • The handler function executes, potentially calling workflows/activities directly or scheduling them.
    • The response returned by the handler function is sent back to the client that made the HTTP request.

Example - triggering the Fibonacci workflow

This example demonstrates a webhook endpoint that triggers the example Fibonacci workflow . It showcases both direct calls and scheduled execution of a workflow function.

WIT File (wit/template_fibo.wit):

package any:any;

world any {
    import template-fibo:workflow/fibo-workflow-ifc;
    import template-fibo:workflow-obelisk-ext/fibo-workflow-ifc;
    import obelisk:log/log@1.0.0;
}

Rust Code (src/lib.rs):

#[handler]
fn handle(_req: Request) -> Result<Response, ErrorCode> {
    let n = std::env::var("N")
        .expect("env var `N` must be set by Obelisk if routes are configured properly")
        .parse()
        .expect("parameter `N` must be of type u8");

    let iterations = std::env::var("ITERATIONS")
        .expect("env var `ITERATIONS` must be set by Obelisk if routes are configured properly")
        .parse()
        .expect("parameter `ITERATIONS` must be of type u32");

    let fibo_res = if n >= 10 {
        println!("scheduling");
        let execution_id = workflow_ext::fiboa_schedule(ScheduleAt::Now, n, iterations);
        format!("scheduled: {}", execution_id.id)
    } else if n > 1 {
        // Call the execution directly.
        println!("direct call");
        let fibo_res = workflow::fiboa(n, iterations);
        format!("direct call: {fibo_res}")
    } else {
        println!("hardcoded");
        "hardcoded: 1".to_string() // For performance testing - no activity is called
    };
    info(&format!("Sending response {fibo_res}"));
    Response::builder().body(fibo_res).build()
}

obelisk.toml (Excerpt):

[[http_server]]
name = "external"
listening_addr = "0.0.0.0:9000"

[[webhook_endpoint]]
name = "fibo"
location.path = "target/wasm32-wasip2/release/fibo_webhook_endpoint.wasm"  # Or use location.oci
http_server = "external"
routes = [{ methods = ["GET"], route = "/fibo/:N/:ITERATIONS" }] # Path parameters are captured

Project Template:

You can quickly create a new webhook endpoint project using cargo-generate and the webhook template:

cargo generate obeli-sk/obelisk-templates fibo/webhook_endpoint --name mywebhook
cd mywebhook
cargo build --release
obelisk server run

Then, you can test it with curl:

curl localhost:9000/fibo/5/1  # Direct call
curl localhost:9000/fibo/12/1 # Scheduled execution

Example - Stargazers demo

The webhook endoint in stargazers demo repository shows the integration with GitHub, specifically:

Key Differences from Workflows

FeatureWorkflowsWebhook Endpoints
TriggeringExplicit - CLI, Web UI, gRPCIncoming HTTP Request
Function ExtensionsFull support (join sets)Limited to direct calls or scheduling
PurposeComplex orchestration, long-running processesHandling HTTP requests, typically just triggering a workflow
ParametersExplicitly declared in WITURL path parameters, configured environment variables

Configuration

Associating Webhook Endpoints with HTTP Servers

Webhook endpoints don't listen for HTTP requests directly. Instead, they are associated with an [[http_server]] instance defined in your obelisk.toml configuration. This association is established using the http_server field within the [[webhook_endpoint]] configuration.

[[http_server]]
name = "my_server"
listening_addr = "0.0.0.0:9000"

[[webhook_endpoint]]
name = "my_endpoint"
http_server = "my_server"  # Associates this endpoint with the "my_server" HTTP server
# ... other webhook endpoint settings ...

Routes Configuration

Each [[webhook_endpoint]] must define one or more routes. These routes determine which incoming HTTP requests will be handled by that endpoint. The routes field is a list of route specifications.

[[webhook_endpoint]]
name = "my_endpoint"
http_server = "my_server"
routes = [
    { methods = ["GET"], route = "/users/{id}" },  # Route with method
    "/products/*",                                 # Route without method (wildcard)
    "/status",                                     # Route without method (exact match)
]
# ... other webhook endpoint settings ...

Route Syntax and Matching

Obelisk supports several ways to define routes:

Route Priority and Matching Order

When an incoming HTTP request arrives, Obelisk checks the configured routes to determine which webhook endpoint should handle it. The matching process follows these rules: