Skip to main content

Quickstart: deploy your first function

A function is the smallest thing you can deploy: one file with one Python function in it, served as an HTTPS endpoint that scales to zero when idle. In this quickstart you'll write it, deploy it with the CLI, call it two ways, and delete it. Total time: a few minutes.

Before you begin

1. Write the function

A Python function is a file named handler.py that defines handle(event). The event is a dictionary built from the incoming request; whatever dictionary you return becomes the JSON response.

mkdir hello-http && cat > hello-http/handler.py <<'EOF'
def handle(event):
who = event.get("name") or event.get("message") or "world"
return {"greeting": f"Hello, {who}!"}
EOF

That's the whole app. No web framework, no server code, no Dockerfile.

2. Deploy it

platformctl functions deploy ./hello-http --name hello-http

You should see:

packaging ./hello-http...
uploading hello-http (0.3 KiB, framework=function)...
build 7c41d9e2-... accepted
status: -> building
status: building -> deploying
status: deploying -> ready
hello-http is ready at http://hello-http.cai-p-ab12cd.svc.cluster.local

The runtime was auto-detected as Python because the folder contains handler.py. Node.js, Go, and Ruby functions use handler.js, handler.go, and handler.rb, but the CLI cannot deploy them yet — see Runtimes for the API call that can.

3. Call it through the platform

platformctl invoke hello-http "ping"

You should see a blank line and a session id:


(session: 3f6c1f0e-9c1c-4f1a-8f2e-2a0d5f6b7c81)

platformctl invoke wraps your text as {"message": "ping"} before delivering it, which is why the handler reads the message key. The blank line is expected: invoke prints only the response's output field, the way an agent answers, and a function's return value has no output key. To see the {"greeting": "Hello, ping!"} your handler returned, call the function's URL, as in step 4.

4. Call it at its public URL

Every workload has a canonical public address of the form https://<name>-<project-short>.apps.codyhill.dev. The CLI does not print it — platformctl status -o json round-trips only the fields on its own agent struct:

platformctl status hello-http -o json
{
"name": "hello-http",
"framework": "function",
"status": "ready",
"url": "http://hello-http.cai-p-ab12cd.svc.cluster.local",
"image": "localhost:30500/hello-http@sha256:9f3c1a2..."
}

kind, runtime, and public_url are in the API's response but are dropped when the CLI decodes it. Ask the API directly instead:

export CAI_API=http://localhost:8081 # or the endpoint your administrator gave you
curl -s "$CAI_API/v1/agents/hello-http?project=$CAI_PROJECT" \
-H "Authorization: Bearer $CAI_TOKEN" | jq -r .public_url

You should see:

https://hello-http-ab12cd.apps.codyhill.dev

Or read it from the function's page in the console.

Workloads are private by default — cluster-local, reachable only through the platform — so that address serves nothing until you publish the function. Publish it by setting CAI_EXPOSE_EXTERNAL on its env, which rolls a new revision:

curl -s -X PATCH "$CAI_API/v1/agents/hello-http/env?project=$CAI_PROJECT" \
-H "Authorization: Bearer $CAI_TOKEN" \
-H 'content-type: application/json' \
-d '{"set": {"CAI_EXPOSE_EXTERNAL": "true"}}'

You should see:

{"agent": "hello-http", "env_updated": true, "env": {"CAI_EXPOSE_EXTERNAL": "true"}}

Once platformctl status hello-http reads ready again, call the function directly — a POSTed JSON body becomes the event dictionary:

export FUNC_URL=https://hello-http-ab12cd.apps.codyhill.dev # your public_url from the curl above
curl -s "$FUNC_URL" -H 'content-type: application/json' -d '{"name":"world"}'

You should see:

{"greeting": "Hello, world!"}

There's also a built-in health check:

curl -s "$FUNC_URL/healthz"

You should see:

{"status": "ok"}
If the public URL doesn't answer

First check that you published it, as above — an unpublished function is cluster-local and its canonical address serves nothing.

The API response for a function (GET /v1/agents/{name}) has two public URL fields: public_url is the canonical address, returned even before the function is published, while external_url appears only once the endpoint is actually reachable over a valid certificate on your install. platformctl status surfaces neither — its output carries only url, the internal cluster address — so query the API or check the console for them. If external_url stays empty after you publish, keep using platformctl invoke and ask your administrator about public endpoints. Alpha honesty: public DNS and TLS are per-install, not guaranteed.

Useful details for later:

  • Return {"statusCode": 404} (plus any other keys) from your handler to control the HTTP status; the default is 200.
  • A plain GET to the function calls your handler with an empty {} event.
  • POST bodies are capped at 8 MiB.
  • Functions can also receive events instead of HTTP calls — see HTTP and events.

5. Clean up

platformctl delete hello-http

You should see:

deleted hello-http

Next steps