Skip to content

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 onSync phase and can trigger 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»

To add HTTP capabilities to an app or a block, include an http property in its definition.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
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»

When you define HTTP capabilities for an app or block, Flows automatically creates a unique endpoint URL.

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:

1
2
const appEndpointUrl = input.app.http.url;
const blockEndpointUrl = input.block.http.url;

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»

The onRequest handler receives an input object containing:

  • App or block context
  • The HTTP request details

Request object structure»

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
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»

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
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»

To respond to an HTTP request, use the http.respond function:

1
await http.respond(requestId, response);

Arguments»

  • requestId: The ID of the request to respond to (from the request object)
  • response: The HTTP response to send

Response object»

1
2
3
4
5
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»

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
// Simple JSON response
await http.respond(request.requestId, {
  statusCode: 200,
  body: { message: "Success", data: result },
});

// HTML response
await http.respond(request.requestId, {
  statusCode: 200,
  headers: {
    "Content-Type": "text/html",
  },
  body: "<html><body><h1>Hello World</h1></body></html>",
});

// Error response
await http.respond(request.requestId, {
  statusCode: 400,
  body: { error: "Bad request", details: "Missing required field" },
});

// No content response
await http.respond(request.requestId, {
  statusCode: 204,
});

Integrating with events»

One of the most powerful patterns is using HTTP endpoints to trigger events within Flows. See the Events documentation for more details on the event system.

HTTP-to-event pattern»

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
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",
    },
  },
};