How to create a function
Create, deploy, and invoke a serverless function on the Rocketstack control plane. Upload code as a ZIP, choose a runtime, and trigger via API or public URL.
Creating the function
This guide walks you through creating, deploying, and invoking a function on the Rocketstack control plane. A function is a serverless unit of compute: you upload your code as a ZIP, choose a runtime, and the platform runs it on each invocation—via the control plane invoke API or a public HTTP URL.
Overview
- Create the function with a
POSTrequest (name, runtime, handler, code ZIP). - Wait until the function status is
ready(creation is asynchronous). - Invoke via the control plane API with a custom payload, or expose a public URL (e.g.
https://fn.rocketstack.dev/...) for HTTP calls.
You can create a function in three ways: Via Console (web UI), Via API (HTTP request), or Via MCP (from an AI assistant or MCP-enabled editor). Choose one and follow the steps below.
Via Console
Use the Rocketstack Console when you prefer a UI over the API—ideal for your first function or quick one-off creates.
- In the Rocketstack Console, open Primitives in the sidebar > Functinos.
- Click Create function.
- Enter a Name (1–64 characters, start with a letter; only letters, numbers, hyphens, underscores).
- Choose a Runtime (e.g.
nodejs20.xorpython3.12). - Set Handler to your entrypoint in the form
fileName.exportedFunction—e.g.index.handlerfor Node orhandler.handlerfor Python. - Upload your function code as a ZIP file (the entrypoint file must be at the root of the ZIP).
- (Optional) To expose a public URL: enable Public and choose the Public method (GET, POST, PATCH, or DELETE).
- Click Create function.
Before you upload
Package your entrypoint (e.g. index.js or handler.py) and any dependencies at the root of the ZIP. The handler value must match that file and export name exactly (see the Code section for details).
After creation
The function appears in the list with status creating until it becomes ready. Open the function to view details; you can trigger a test invocation or check logs from the console. VERIFY: exact labels and availability of test invoke/logs in UI.
Troubleshooting
- Invalid or empty ZIP: Ensure the ZIP is a valid archive and contains your entry file at the root (not inside a nested folder).
- Handler mismatch: The handler string must match the file name and exported function (e.g.
index.handler→index.jswithexport const handlerorexports.handler). - Runtime mismatch: Confirm the selected runtime matches your code (e.g. Node vs Python).
- Creation stays in "creating": Wait a minute and refresh; if it persists, check the console for errors or try a smaller ZIP. VERIFY: actual behavior and support flow.
Alternatives: To create via HTTP, see Via API. To create from an AI assistant or editor, see Via MCP.
Via API
Use the API when you're scripting, integrating with CI/CD, or automating deployments.
Endpoint: POST /v1/functions
Content-Type: multipart/form-data
Use your API base URL (e.g. https://api.rocketstack.dev) with the path above. Send all parameters as form fields; the function code is the only binary field.
Required form fields
| Field | Type | Description |
|---|---|---|
name | string | Function name. 1–64 characters, must start with a letter. Only letters, numbers, hyphens, and underscores. |
runtime | string | One of: nodejs20.x, python3.12. |
handler | string | Entrypoint: for Node.js fileName.exportedFunction (e.g. index.handler); for Python fileName.functionName (e.g. handler.handler). |
code | file | The function code as a ZIP file (binary). |
Optional form fields
| Field | Type | Description |
|---|---|---|
memoryMb | number | 128–10240; default 256. |
timeoutSeconds | number | 1–60; default 10. |
env | string | JSON object of environment variables, e.g. {"KEY":"value"}. |
reservedConcurrency | number | 0 = paused, 1–100 = concurrency cap, omit or null = unreserved. |
description | string | Max 256 characters. |
isPublic | string | Set to "true" to make the function invokable via the public base URL (e.g. fn.rocketstack.dev) without auth. |
publicMethod | string | When isPublic is true, the single HTTP method allowed for that public URL: one of GET, POST, PATCH, DELETE. Default when public is true is POST. |
Example: create with cURL
curl -X POST https://api.rocketstack.dev/v1/functions \
-H "Authorization: Bearer YOUR_API_KEY" \
-F "name=my-api" \
-F "runtime=nodejs20.x" \
-F "handler=index.handler" \
-F "code=@dist.zip"
Response
The API returns 202 Accepted with a body like:
{
"requestId": "...",
"data": {
"resourceId": "...",
"status": "creating"
}
}
Creation is asynchronous. Use resourceId to poll the function resource (e.g. GET /v1/functions/:id) until status is ready before invoking or relying on the public URL.
Alternatives: To create from the web UI, see Via Console. To create from an AI assistant or editor, see Via MCP.
Via MCP
Use MCP (Model Context Protocol) when you want to create or update a function from an MCP-enabled client such as Claude, Cursor, or Windsurf—without leaving your chat or editor.
- Configure the Rocketstack MCP server in your client (e.g. add the server with your
ROCKETSTACK_API_KEY). VERIFY: link to full MCP setup if available. - In your client, use the deploy flow (e.g. ask to "deploy my function" or use a
functions.deploy-style tool if exposed). - Provide the name, runtime (e.g.
nodejs20.xorpython3.12), and handler (e.g.index.handler). The client typically bundles your code and uploads it as a ZIP. - (Optional) Specify if the function should be public and which public method to allow.
The same packaging rules apply: your entrypoint file (e.g. index.js or handler.py) and dependencies should be at the root of the bundle (see the Code section). After creation, the function appears in the Console and can be invoked via API or public URL like any other.
For full MCP setup (config, tools, and supported clients), see MCP Integration in the docs.
Alternatives: To create from the web UI, see Via Console. To create via HTTP, see Via API.
Code
Code layout and ZIP
- The ZIP must contain your entrypoint file at the root (e.g.
index.jsorhandler.py) and any dependencies. - Node.js: The file named in the handler (e.g.
index) must export the handler function—e.g.export const handler = async (event) => { ... }orexports.handler = async (event) => { ... }. - Python: The file named in the handler (e.g.
handler) must define the function—e.g.def handler(event, context): ....
Handler and entrypoint
The handler is the module and function the runtime runs for each invocation.
- Node.js:
fileName.exportedFunction— e.g.index.handlermeansindex.jswithexport const handler = ...orexports.handler = .... - Python:
fileName.functionName— e.g.handler.handlermeanshandler.pywithdef handler(event, context): ....
The handler receives an event (and in Python a context). For public HTTP invocations (see below), the event has a fixed shape. For other invocations (e.g. control plane invoke API or queue/cron triggers), the event is the payload sent by the caller.
Handler and methods
- Public URL: When
isPublicis true, you set one HTTP method per function viapublicMethod(GET,POST,PATCH, orDELETE). Requests with a different method receive a method-not-allowed response before your handler runs. - Invoke API: The same function can be invoked with
POST /v1/functions/:id/invokeand a custom JSON payload. In that case the event is that payload, not the HTTP-shaped object below.
Event object (public HTTP)
When the function is invoked via the public URL (e.g. https://fn.rocketstack.dev/:tenantToken/:functionName), the platform passes a single event object:
| Field | Type | Description |
|---|---|---|
method | 'GET' | 'POST' | 'PATCH' | 'DELETE' | The HTTP method of the request. |
query | Record<string, string | string[]> | Query string parameters. |
body | any | Request body (parsed JSON or raw). For GET/DELETE it may be null. |
Your handler can branch on event.method and use event.query and event.body to implement the response.
Example: Node.js handler (public HTTP)
// index.js
export const handler = async (event) => {
const { method, query, body } = event;
if (method === 'GET') {
return {
statusCode: 200,
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ message: 'Hello', query })
};
}
if (method === 'POST') {
return {
statusCode: 200,
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ received: body })
};
}
return {
statusCode: 405,
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ error: 'Method not allowed' })
};
};
Example: Python handler (public HTTP)
# handler.py
import json
def handler(event, context):
method = event.get('method', 'GET')
query = event.get('query') or {}
body = event.get('body')
if method == 'GET':
return {
'statusCode': 200,
'headers': {'Content-Type': 'application/json'},
'body': json.dumps({'message': 'Hello', 'query': query})
}
if method == 'POST':
return {
'statusCode': 200,
'headers': {'Content-Type': 'application/json'},
'body': json.dumps({'received': body})
}
return {
'statusCode': 405,
'headers': {'Content-Type': 'application/json'},
'body': json.dumps({'error': 'Method not allowed'})
}
Handler return value
The handler can return any JSON-serializable value.
For public HTTP use, a common pattern is to return an object with:
statusCode– HTTP status (e.g. 200, 404).headers– Object of response headers (e.g.{ 'Content-Type': 'application/json' }).body– Response body as a string (e.g.JSON.stringify(...)).
The gateway sends the invoke result back to the client: the HTTP status of the invoke call is typically 200, and the response body is your handler’s return value. If the client expects a normal HTTP response, it may need to read response.data.statusCode and response.data.body until the gateway maps the handler’s statusCode and body to the actual HTTP response.
For invoke API calls, the returned value is simply the JSON your handler returns.
Invoking your function
- Control plane:
POST /v1/functions/:id/invokewith an API key and a JSON body. The body is passed as the event to your handler. - Public URL: If
isPublicis"true", callhttps://fn.rocketstack.dev/:tenantToken/:functionNamewith the method set inpublicMethod. The platform builds the{ method, query, body }event and passes it to your handler.
Summary
| Step | Action |
|---|---|
| 1 | POST /v1/functions with multipart/form-data: name, runtime, handler, code (ZIP). Optionally set memoryMb, timeoutSeconds, env, isPublic, publicMethod, etc. |
| 2 | Poll GET /v1/functions/:resourceId until status is ready. |
| 3 | Invoke via POST /v1/functions/:id/invoke with a custom payload, or via the public URL with the configured HTTP method. |
Use the handler string and runtime (Node or Python) to match your entrypoint file and export. For public HTTP, handle the { method, query, body } event and return a value (e.g. statusCode, headers, body) that your client or gateway can use.