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.
https://api.swiftlambda.comAuthentication
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 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
- Open Settings → API keys in the console.
- Click Create new key.
- 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.
- 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).
{
"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}/runs—modeis not"oneShot"or"service". - invalid_arguments
- 400 from
POST /lambdas/{LAMBDA_ID}/runs— theargumentsarray 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
oneShotmode 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.
/lambdas/{LAMBDA_ID}/runsStart 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 -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 -X POST https://api.swiftlambda.com/lambdas/{LAMBDA_ID}/runs \
-H "X-API-Key: {API_KEY}" \
-H "Content-Type: application/json" \
-d '{ "mode": "service" }'{
"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}"
}/lambdas/{LAMBDA_ID}/runsList runs
Returns runs newest first. Optional ?limit=N (default 20, capped at 50) and ?state=queued|running|succeeded|failed|stopped|timedOut filters.
curl "https://api.swiftlambda.com/lambdas/{LAMBDA_ID}/runs?state=running" \
-H "X-API-Key: {API_KEY}"/lambdas/{LAMBDA_ID}/runs/{RUN_ID}Get run
Poll target. Returns the current state, phase, exit code (when terminal), and timing.
curl https://api.swiftlambda.com/lambdas/{LAMBDA_ID}/runs/{RUN_ID} \
-H "X-API-Key: {API_KEY}"/lambdas/{LAMBDA_ID}/runs/{RUN_ID}/stopStop run
Stops a running (or queued) execution. Returns 409 not_stoppable if the run is already terminal.
curl -X POST https://api.swiftlambda.com/lambdas/{LAMBDA_ID}/runs/{RUN_ID}/stop \
-H "X-API-Key: {API_KEY}" \
-d '{}'/lambdas/{LAMBDA_ID}/runs/{RUN_ID}/logsStream run logs
Returns log chunks in order. Use afterSequence to paginate forward. Optional ?limit=N (default 200, max 500).
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.
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 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 always8080). Bind on0.0.0.0, not127.0.0.1, so the proxy can reach you across the container boundary.LAMBDA_RUN_UUID— theuuidof 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.
// 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"),
]
),
]
)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:
# 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.