# Integrating SpacetimeDB with External Services: A Complete Guide to HTTP Egress

> Learn to integrate SpacetimeDB with external HTTP services. Call webhooks, REST APIs, and third-party data sources securely from your SpacetimeDB procedures.

- Repository: [Clockwork Labs/SpacetimeDB](https://github.com/clockworklabs/SpacetimeDB)
- Tags: how-to-guide
- Published: 2026-03-09

---

**SpacetimeDB procedures can call external HTTP APIs using the built-in `HttpClient` available on `ProcedureContext`, enabling seamless integration with webhooks, REST services, and third-party data sources while maintaining the platform's sandboxed WebAssembly security model.**

Integrating SpacetimeDB with external services allows your database logic to fetch configuration, trigger notifications, or enrich data in real-time. Despite running inside a sandboxed WebAssembly module, SpacetimeDB provides a **built-in HTTP client** exposed through the `ProcedureContext` struct, enabling outbound requests without requiring external dependencies like `reqwest`.

## How SpacetimeDB HTTP Egress Works

The HTTP client architecture bridges the WebAssembly sandbox and the host runtime through a tightly controlled interface defined in [`crates/bindings/src/lib.rs`](https://github.com/clockworklabs/SpacetimeDB/blob/main/crates/bindings/src/lib.rs) and [`crates/bindings/src/http.rs`](https://github.com/clockworklabs/SpacetimeDB/blob/main/crates/bindings/src/http.rs).

### The ProcedureContext Interface

Every SpacetimeDB procedure receives a mutable reference to `ProcedureContext`, which contains request-specific metadata and the `http: HttpClient` field. According to the source code in [`crates/bindings/src/lib.rs`](https://github.com/clockworklabs/SpacetimeDB/blob/main/crates/bindings/src/lib.rs) (lines 80-82), this context provides the gateway for all outbound communication:

```rust
pub struct ProcedureContext {
    pub http: HttpClient,
    // ... identity, timestamp, etc.
}

```

### HttpClient Implementation and BSATN Serialization

The `HttpClient` struct, defined in [`crates/bindings/src/http.rs`](https://github.com/clockworklabs/SpacetimeDB/blob/main/crates/bindings/src/http.rs) (lines 27-34), acts as a thin wrapper that serializes standard `http::Request` objects into SpacetimeDB's wire format (BSATN). The client delegates actual network execution to the host via `spacetimedb_bindings_sys::procedure::http_request`.

The conversion process in [`crates/bindings/src/http.rs`](https://github.com/clockworklabs/SpacetimeDB/blob/main/crates/bindings/src/http.rs) (lines 39-71) preserves HTTP method, URI, headers, and an optional `Timeout` extension, ensuring compatibility with standard HTTP semantics while maintaining sandbox constraints.

### Request Timeouts and Error Handling

SpacetimeDB enforces a **hard limit of 500 ms** for all outbound HTTP requests to prevent blocking the database runtime. Procedures can specify a lower timeout by adding a `http::Timeout` extension to the request builder, as implemented in [`crates/bindings/src/http.rs`](https://github.com/clockworklabs/SpacetimeDB/blob/main/crates/bindings/src/http.rs) (lines 31-35).

The error model distinguishes between transport failures and HTTP error codes. According to [`crates/bindings/src/http.rs`](https://github.com/clockworklabs/SpacetimeDB/blob/main/crates/bindings/src/http.rs) (lines 37-40), `HttpClient::send` returns `Result<Response, Error>` where `Error` indicates network-level failures (DNS resolution, connection refused), while HTTP status codes like 404 or 500 are returned as successful `Response` objects that the procedure must handle explicitly.

## Practical Examples: Calling External APIs from SpacetimeDB

The following examples demonstrate how to integrate SpacetimeDB with external services using the `ProcedureContext` HTTP client.

### Simple GET Request to Fetch External Data

This example retrieves repository information from the GitHub API. The `ctx.http.get` shortcut constructs a basic GET request without custom headers:

```rust
use spacetimedb::{procedure, ProcedureContext};

#[procedure]
fn fetch_github_api(ctx: &mut ProcedureContext) {
    match ctx.http.get("https://api.github.com/repos/clockworklabs/SpacetimeDB") {
        Ok(response) => {
            let (parts, body) = response.into_parts();
            log::info!("GitHub returned {} bytes, status {}", body.into_bytes().len(), parts.status);
        }
        Err(err) => {
            log::error!("Failed to call GitHub API: {err}");
        }
    }
}

```

The response body implements `into_bytes()` and `into_string_lossy()` methods for content inspection.

### POST Request with JSON Payload and Custom Timeout

When integrating with webhooks or REST APIs that require POST methods, use the `http::Request::builder()` pattern to set headers, method, and timeout extensions. This example sends a JSON payload with a 200 ms timeout, overriding the default 500 ms limit:

```rust
use spacetimedb::{procedure, ProcedureContext, http::Timeout};
use std::time::Duration;
use http::header::CONTENT_TYPE;

#[procedure]
fn post_to_webhook(ctx: &mut ProcedureContext) {
    let json = r#"{"event":"user_signup","user_id":42}"#;

    let request = http::Request::builder()
        .method(http::Method::POST)
        .uri("https://example.com/webhook")
        .header(CONTENT_TYPE, "application/json")
        .extension(Timeout::from(Duration::from_millis(200)))
        .body(json.to_string())
        .expect("invalid request");

    match ctx.http.send(request) {
        Ok(resp) => {
            let (parts, body) = resp.into_parts();
            log::info!("Webhook responded {} – {}", parts.status, body.into_string_lossy());
        }
        Err(e) => log::error!("Webhook POST failed: {e}"),
    }
}

```

The `Timeout` extension accepts any `Duration` up to 500 ms. Requests exceeding this limit trigger a host-level cancellation.

### Handling HTTP Error Responses Gracefully

HTTP status codes such as 404 or 500 are returned as `Ok(Response)` variants, not as `Err` values. Procedures must explicitly check `status().is_success()` to distinguish between successful and failed application-level responses:

```rust
use spacetimedb::{procedure, ProcedureContext};

#[procedure]
fn fetch_with_status_check(ctx: &mut ProcedureContext) {
    let url = "https://httpbin.org/status/404";

    match ctx.http.get(url) {
        Ok(resp) => {
            let status = resp.status();
            if status.is_success() {
                let body = resp.into_body().into_string_lossy();
                log::info!("Success: {body}");
            } else {
                log::warn!("Request to {url} returned HTTP {status}");
            }
        }
        Err(e) => log::error!("Network error: {e}"),
    }
}

```

Transport errors (DNS failures, connection timeouts, TLS errors) return `Err(Error)`, while HTTP semantic errors return `Ok(Response)` with the appropriate status code.

## Key Source Files for HTTP Integration

The following files in the `clockworklabs/SpacetimeDB` repository define the HTTP egress functionality:

| File | Purpose |
|------|---------|
| [`crates/bindings/src/lib.rs`](https://github.com/clockworklabs/SpacetimeDB/blob/main/crates/bindings/src/lib.rs) | Defines `ProcedureContext` with the `http: HttpClient` field (lines 80-82). |
| [`crates/bindings/src/http.rs`](https://github.com/clockworklabs/SpacetimeDB/blob/main/crates/bindings/src/http.rs) | Implements `HttpClient`, request conversion, BSATN serialization, timeout handling, and response reconstruction. |
| [`crates/bindings/src/rt.rs`](https://github.com/clockworklabs/SpacetimeDB/blob/main/crates/bindings/src/rt.rs) | Contains macro-generated glue that injects the `http` field into procedure contexts at runtime. |
| [`crates/smoke-tests/tests/http_egress.rs`](https://github.com/clockworklabs/SpacetimeDB/blob/main/crates/smoke-tests/tests/http_egress.rs) | Integration tests validating outbound HTTP functionality, including IP disallow lists. |

## Summary

- **SpacetimeDB procedures** interact with external services via the `HttpClient` available on `ProcedureContext`.
- **BSATN serialization** converts standard `http::Request` objects into the wire format used by the host runtime.
- **500 ms hard limit** caps all outbound requests, with optional lower timeouts set via the `Timeout` extension.
- **Error handling** distinguishes transport failures (`Err`) from HTTP error codes (`Ok(Response)`).
- **Zero external dependencies** are required; the client is built into the SpacetimeDB bindings.

## Frequently Asked Questions

### Can SpacetimeDB procedures accept incoming HTTP requests?

No. The HTTP client exposed on `ProcedureContext` is strictly for **outbound egress**. SpacetimeDB modules expose callable procedures to clients through the SpacetimeDB SDK and WebSocket connections, not through direct inbound HTTP endpoints. Incoming requests must use the standard SpacetimeDB client libraries.

### What is the maximum timeout for HTTP requests in SpacetimeDB?

The host enforces a **hard ceiling of 500 milliseconds** for all outbound HTTP requests to prevent blocking the database runtime. Procedures can specify a shorter timeout by attaching a `Timeout` extension to the request builder, but cannot exceed the 500 ms limit.

### How does SpacetimeDB serialize HTTP requests for WebAssembly?

SpacetimeDB uses **BSATN** (Binary SpacetimeDB Algebraic Notation) to serialize `http::Request` objects before passing them to the host. The `HttpClient` in [`crates/bindings/src/http.rs`](https://github.com/clockworklabs/SpacetimeDB/blob/main/crates/bindings/src/http.rs) handles this conversion, transforming standard HTTP parts (method, URI, headers) into the wire format expected by `spacetimedb_bindings_sys::procedure::http_request`.

### Can I use external HTTP client libraries like reqwest in SpacetimeDB?

No. SpacetimeDB procedures run inside a **sandboxed WebAssembly module** that cannot perform unsandboxed network I/O. You must use the built-in `HttpClient` provided on `ProcedureContext`, which delegates network operations to the trusted host runtime. This design ensures deterministic execution and security while still enabling external service integration.