# HTTP

The HTTP service in Flows provides a way for apps and blocks to receive and respond to HTTP requests from external systems. This allows your Flows applications to expose their functionality to the outside world. Receiving webhooks and exposing APIs become straightforward with this service.

HTTP endpoints are often set up during the [lifecycle](./lifecycle) `onSync` phase and can trigger [events](./events) to integrate with workflows.

This service is essential for:

 - Building webhook receivers for services like GitHub, Stripe, or Slack;
 - Creating API endpoints for external applications;
 - Building integration bridges to external systems using complex HTTP-based authentication flows like OAuth or OIDC;

## Adding HTTP capabilities
[Section titled “Adding HTTP capabilities”](#adding-http-capabilities)
To add HTTP capabilities to an app or a block, include an `http` property in its definition.


**

```
import { http } from "@slflows/sdk/v1";
{  // ...  http: {    onRequest: async ({ request }) => {      // Respond to the request      await http.respond(request.requestId, {        statusCode: 200,        body: { message: `Received ${request.method} on ${request.path}` },      });    },  },};
```

Bring your own routing

Each entity receives all HTTP requests to its endpoint URL. Your app code must implement any path or method-based routing logic by inspecting `request.path` and `request.method`.

## HTTP endpoints and URLs
[Section titled “HTTP endpoints and URLs”](#http-endpoints-and-urls)
When you define HTTP capabilities for an app or block, Flows automatically creates a unique endpoint URL.

### URL structure
[Section titled “URL structure”](#url-structure)
 - **App endpoints**: Flows generates a unique URL for each app installation
 - **Block endpoints**: Each block with HTTP capabilities gets its own path within the app’s URL

You can access these URLs programmatically wherever `app` and `block` contexts are available:


**

```
const appEndpointUrl = input.app.http.url;const blockEndpointUrl = input.block.http.url;
```

### URL placeholders in app instructions
[Section titled “URL placeholders in app instructions”](#url-placeholders-in-app-instructions)
When configuring apps, you can use these placeholders in instruction text that will be replaced with actual values:

 - `{appEndpointUrl}` - The complete endpoint URL (e.g., `https://app-123.flows.example.com/`)
 - `{appEndpointHost}` - The hostname of the endpoint (e.g., `app-123.flows.example.com`)

These placeholders are useful for providing users with specific URLs they need to configure in external services.

## Handling requests
[Section titled “Handling requests”](#handling-requests)
The `onRequest` handler receives an input object containing:

 - App or block context
 - The HTTP request details

### Request object structure
[Section titled “Request object structure”](#request-object-structure)

**

```
interface HTTPRequest {  requestId: string; // Unique ID for this request  path: string; // Request path  method: string; // HTTP method (GET, POST, etc.)  headers: Record<string, string>; // HTTP headers  query: Record<string, string>; // Query parameters  params: Record<string, string>; // Path parameters  rawBody: string; // Raw request body  body: any; // Parsed body (if JSON)}
```

### Example: basic request handling
[Section titled “Example: basic request handling”](#example-basic-request-handling)

**

```
onRequest: async ({ request }) => {  // Access request details  console.log(`Method: ${request.method}`);  console.log(`Path: ${request.path}`);  console.log(`Query parameters:`, request.query);  console.log(`Headers:`, request.headers);
  // Access the body  if (request.method === "POST") {    console.log("Request body:", request.body);  }
  // Send a response  await http.respond(request.requestId, {    statusCode: 200,    headers: {      "Content-Type": "application/json",    },    body: {      message: "Request processed successfully",    },  });};
```

## Sending responses
[Section titled “Sending responses”](#sending-responses)
To respond to an HTTP request, use the `http.respond` function:


**

```
await http.respond(requestId, response);
```

### Arguments
[Section titled “Arguments”](#arguments)
 - `requestId`: The ID of the request to respond to (from the request object)
 - `response`: The HTTP response to send

### Response object
[Section titled “Response object”](#response-object)

**

```
interface HTTPResponse {  statusCode?: number; // Default: 200  headers?: Record<string, string>; // Response headers  body?: string | object | unknown; // Response body}
```

Need binary responses?

If the body is a `Uint8Array`, it will be sent as binary data.

### Response examples
[Section titled “Response examples”](#response-examples)

**

```
// Simple JSON responseawait http.respond(request.requestId, {  statusCode: 200,  body: { message: "Success", data: result },});
// HTML responseawait http.respond(request.requestId, {  statusCode: 200,  headers: {    "Content-Type": "text/html",  },  body: "<html><body><h1>Hello World</h1></body></html>",});
// Error responseawait http.respond(request.requestId, {  statusCode: 400,  body: { error: "Bad request", details: "Missing required field" },});
// No content responseawait http.respond(request.requestId, {  statusCode: 204,});
```

## Integrating with events
[Section titled “Integrating with events”](#integrating-with-events)
One of the most powerful patterns is using HTTP endpoints to trigger events within Flows. See the [Events documentation](./events) for more details on the event system.

### HTTP-to-event pattern
[Section titled “HTTP-to-event pattern”](#http-to-event-pattern)

**

```
import { AppBlock, http, events } from "@slflows/sdk/v1";
export const webhookBlock: AppBlock = {  name: "Webhook Receiver",
  http: {    onRequest: async (input): Promise<void> => {      const { request } = input;
      // Validate the request      if (request.method !== "POST") {        await http.respond(request.requestId, {          statusCode: 405,          body: { error: "Method not allowed" },        });        return;      }
      try {        // Process the webhook payload        const payload = request.body;
        // Emit an event with the webhook data        await events.emit(          {            type: "webhook_received",            source: request.headers["user-agent"],            payload,          },        );
        // Respond to the webhook sender        await http.respond(request.requestId, {          statusCode: 200,          body: { status: "received" },        });      } catch (error) {        console.error("Error processing webhook:", error);
        await http.respond(request.requestId, {          statusCode: 500,          body: { error: "Internal server error" },        });      }    },  },
  outputs: {    default: {      name: "Webhook Received",      description: "Emitted when a webhook is received",    },  },};
```