Reference

API documentation

Invoke your compiled SwiftLambda over HTTP. Start a one-shot or a service-mode run, watch its status, stream its logs, stop it, or proxy traffic to a long-lived service. Project setup and builds happen in the web console; this API takes over once you have a successful build.

Getting started

Base URL

All requests go to the SwiftLambda backend at the URL below. This is the same value the web console points at via NEXT_PUBLIC_API_URL. Examples on this page are copy-paste ready.

base
https://api.swiftlambda.com

Authentication

Every endpoint on this page is authenticated with an X-API-Key header. Get one from the API keys page in the web console, then attach it to every request:

cURL · authenticated
curl https://api.swiftlambda.com/lambdas/{LAMBDA_ID}/runs \
  -H "X-API-Key: {API_KEY}"

A missing or unrecognized key returns 401. A revoked or expired key returns 401. A key used on a project outside its allow-list returns 403.

API keys

API keys are created, listed, and revoked from the API keys page in the console. Each key has:

  • A name — for your own reference (e.g. "CI deploy", "staging monitor").
  • An optional expiry — pick a preset (30 days, 90 days, 1 year) or set a custom number of days. Without an expiry the key is valid until you revoke it.
  • An optional project restriction — limit the key to specific lambda projects. A restricted key on any other project returns 403. Without a restriction the key works on every project you own.

Creating a key

  1. Open Settings → API keys in the console.
  2. Click Create new key.
  3. Set a name. Pick an expiry preset (or leave as Never). Optionally tick Restrict to specific projects and select which projects this key may access.
  4. Submit. The full secret is shown exactly once and auto-copied to your clipboard. Save it somewhere safe (a password manager or your CI secret store) before closing the dialog — afterwards only the key prefix is visible and the secret cannot be recovered.

Revoking a key

On the API keys page click Revoke next to the key. Revocation is immediate and irreversible — any service still using the key starts getting 401 on its next request. To rotate a key, create the new key first, deploy it, then revoke the old one.

Errors

Errors return a non-2xx HTTP status and a JSON body of the shape { "error": true, "reason": "<key>" }. The reason is a stable machine-readable string you can branch on. Some errors come back as plain 4xx with no body (badly formed requests, missing path parameters).

JSON · 409 conflict
{
  "error": true,
  "reason": "no_succeeded_compilation"
}

Notable status codes: 400 bad input, 401 auth required or expired, 403 forbidden, 404 not found, 409 conflict (e.g. run not stoppable, project has no successful build), 500 backend failure.

Stable reason codes

no_succeeded_compilation
409 from POST /lambdas/{LAMBDA_ID}/runs — the project has no successful build yet. Trigger a build in the console first.
invalid_mode
400 from POST /lambdas/{LAMBDA_ID}/runsmode is not "oneShot" or "service".
invalid_arguments
400 from POST /lambdas/{LAMBDA_ID}/runs — the arguments array failed validation.
not_stoppable
409 from POST …/runs/{RUN_ID}/stop — run is already terminal.
instance_not_found
404 from the service-mode proxy — no run with that id.
instance_not_servable
409 from the proxy — the run was started in oneShot mode and has no HTTP server.
instance_not_running
409 from the proxy — the run has terminated.
instance_unreachable
502 from the proxy — upstream container is not accepting connections.
instance_timeout
504 from the proxy — upstream call exceeded the remaining run timeout.
unsupported_protocol
400 from the proxy — the request used a protocol the proxy cannot forward (e.g. WebSocket).

Status keys

Every run carries a state field. The values you'll observe:

Run state

  • queued waiting to be scheduled
  • running container active
  • succeeded exited 0
  • failed exited non-zero
  • stopped stopped via /stop
  • timedOut exceeded runTimeoutSeconds

Runs

A run is one execution of the most recently built binary. Two modes: oneShot (start, capture stdout, exit) and service (long-lived, proxied behind an HTTP URL). The project must have at least one successful build (kicked off from the web console) before runs can start; otherwise POST /lambdas/{LAMBDA_ID}/runs returns 409 no_succeeded_compilation.

Get {LAMBDA_ID} from the project page in the console — it's the Lambda ID shown under the target name.

POST/lambdas/{LAMBDA_ID}/runs

Start run

Schedules a run of the latest successful build. Returns 409 no_succeeded_compilation if the project has never compiled successfully. Returns 400 invalid_mode or invalid_arguments on bad input.

cURL · one-shot
curl -X POST https://api.swiftlambda.com/lambdas/{LAMBDA_ID}/runs \
  -H "X-API-Key: {API_KEY}" \
  -H "Content-Type: application/json" \
  -d '{
    "mode": "oneShot",
    "arguments": ["--flag", "value"]
  }'
cURL · service mode
curl -X POST https://api.swiftlambda.com/lambdas/{LAMBDA_ID}/runs \
  -H "X-API-Key: {API_KEY}" \
  -H "Content-Type: application/json" \
  -d '{ "mode": "service" }'
200 · response
{
  "run": {
    "uuid": "{RUN_ID}",
    "project": "{LAMBDA_ID}",
    "compilation": "{COMPILATION_ID}",
    "targetName": "my-app",
    "binaryPath": "/binaries/{LAMBDA_ID}/my-app",
    "dockerImage": "swift:5.10-jammy",
    "arguments": ["--flag", "value"],
    "state": "queued",
    "phase": "queued",
    "runTimeoutSeconds": 60,
    "mode": "oneShot",
    "containerPort": null,
    "hostPort": null,
    "publicAccess": false,
    "startedAt": null,
    "finishedAt": null,
    "exitCode": null,
    "errorSummary": null,
    "logsTruncated": false,
    "created": 1716300300,
    "updated": 1716300300
  },
  "jobId": "{JOB_ID}"
}
GET/lambdas/{LAMBDA_ID}/runs

List runs

Returns runs newest first. Optional ?limit=N (default 20, capped at 50) and ?state=queued|running|succeeded|failed|stopped|timedOut filters.

cURL
curl "https://api.swiftlambda.com/lambdas/{LAMBDA_ID}/runs?state=running" \
  -H "X-API-Key: {API_KEY}"
GET/lambdas/{LAMBDA_ID}/runs/{RUN_ID}

Get run

Poll target. Returns the current state, phase, exit code (when terminal), and timing.

cURL
curl https://api.swiftlambda.com/lambdas/{LAMBDA_ID}/runs/{RUN_ID} \
  -H "X-API-Key: {API_KEY}"
POST/lambdas/{LAMBDA_ID}/runs/{RUN_ID}/stop

Stop run

Stops a running (or queued) execution. Returns 409 not_stoppable if the run is already terminal.

cURL
curl -X POST https://api.swiftlambda.com/lambdas/{LAMBDA_ID}/runs/{RUN_ID}/stop \
  -H "X-API-Key: {API_KEY}" \
  -d '{}'
GET/lambdas/{LAMBDA_ID}/runs/{RUN_ID}/logs

Stream run logs

Returns log chunks in order. Use afterSequence to paginate forward. Optional ?limit=N (default 200, max 500).

cURL
curl "https://api.swiftlambda.com/lambdas/{LAMBDA_ID}/runs/{RUN_ID}/logs?afterSequence=-1" \
  -H "X-API-Key: {API_KEY}"

Service mode + proxy URL

When you start a run with { "mode": "service" }, the backend keeps the container alive and exposes its HTTP server behind a proxy URL. The proxy accepts GET, POST, PUT, DELETE, PATCH, HEAD, and OPTIONS. Anything after /api is forwarded to the upstream container.

base
https://api.swiftlambda.com/instances/{RUN_ID}/api/**

By default the proxy requires X-API-Key like any other call — owner-only. Set publicInstances: true on the project (before starting the run) to allow unauthenticated access. The run snapshots the project's publicInstances flag at creation time, so toggling it later doesn't affect runs that are already live.

cURL
curl https://api.swiftlambda.com/instances/{RUN_ID}/api/hello \
  -H "X-API-Key: {API_KEY}"

Configure your lambda to receive proxied requests

For service-mode runs the container is given three environment variables when it starts:

  • PORT — the TCP port your HTTP server must bind to (currently always 8080). Bind on 0.0.0.0, not 127.0.0.1, so the proxy can reach you across the container boundary.
  • LAMBDA_RUN_UUID — the uuid of the run hosting this container, useful for log correlation.
  • LAMBDA_PROJECT_UUID — the project's lambda id (the same value you pass as {LAMBDA_ID} elsewhere on this page).

The proxy strips /instances/{RUN_ID}/api from the URL before forwarding, so a client request to …/api/hello arrives at your server as GET /hello. Request method, headers, query string, and body are all forwarded unchanged (except for hop-by-hop headers like Host and Connection). The response your server returns is forwarded back to the client unchanged.

Below is a minimal Vapor app that satisfies the contract. Any HTTP server library works — Hummingbird, swift-nio directly, or a plain custom server — as long as you bind to $PORT on 0.0.0.0.

Package.swift
// swift-tools-version:5.10
import PackageDescription

let package = Package(
    name: "my-lambda",
    platforms: [.macOS(.v13)],
    products: [
        .executable(name: "my-lambda", targets: ["my-lambda"]),
    ],
    dependencies: [
        .package(url: "https://github.com/vapor/vapor.git", from: "4.99.0"),
    ],
    targets: [
        .executableTarget(
            name: "my-lambda",
            dependencies: [
                .product(name: "Vapor", package: "vapor"),
            ]
        ),
    ]
)
Sources/my-lambda/main.swift
import Foundation
import Vapor

let app = try await Application.make(.detect())

// Bind to the port SwiftLambda allocates via the PORT env var.
// Fall back to 8080 when running locally outside the platform.
let port = Int(ProcessInfo.processInfo.environment["PORT"] ?? "8080") ?? 8080
app.http.server.configuration.hostname = "0.0.0.0"
app.http.server.configuration.port = port

// Routes are defined relative to the path the proxy forwards, e.g.
// a client GET https://api.swiftlambda.com/instances/{RUN_ID}/api/hello
// arrives here as GET /hello.
app.get("hello") { req async -> String in
    let runID = ProcessInfo.processInfo.environment["LAMBDA_RUN_UUID"] ?? "local"
    return "Hello from run \(runID)\n"
}

app.post("echo") { req async throws -> Response in
    let body = req.body.string ?? ""
    return Response(status: .ok, body: .init(string: body))
}

try await app.execute()
try await app.asyncShutdown()

Build and ship this lambda in the console, then start a service-mode run and call it through the proxy:

cURL · end-to-end
# 1. Start the service run (returns {RUN_ID})
curl -X POST https://api.swiftlambda.com/lambdas/{LAMBDA_ID}/runs \
  -H "X-API-Key: {API_KEY}" \
  -H "Content-Type: application/json" \
  -d '{ "mode": "service" }'

# 2. Hit the routes you defined
curl https://api.swiftlambda.com/instances/{RUN_ID}/api/hello \
  -H "X-API-Key: {API_KEY}"

curl -X POST https://api.swiftlambda.com/instances/{RUN_ID}/api/echo \
  -H "X-API-Key: {API_KEY}" \
  -H "Content-Type: application/json" \
  -d '{"message":"hi"}'

If the container is up but no process is listening on $PORT, the proxy returns 502 instance_unreachable. Bind your server before doing any slow startup work, then complete initialization in the background — that way readiness is correctly reflected to callers.

To shut the service down, call stop-run with the run id.