Skip to main content

Write a function

Write the Python that an app-mode function runs, save it as a new version, and move between the versions you have saved.

Prerequisites#

  • An app-mode function to edit. To create one, follow Create, run, and delete a function.
  • An organization owner or admin account, or another cloud user with access to the site.
  • An API key or JWT for that account.

Write the entry point#

Define a function called main that takes exactly three parameters:

main.py
def main(params, user_data, sdk_client):
return {"ok": True}
ParameterCarries
paramsThe caller's input, plus keys the platform adds
user_dataThe authenticated caller
sdk_clientA TaruviBase SDK client that acts as that caller

Return a JSON-serializable value. It becomes the data field of a synchronous response, and the stored result of an asynchronous one. A falsy return value (None, {}, [], 0, False, or "") comes back as the whole execution record, {result, stdout, stderr, logs, success}, instead. Return a non-empty object, such as {"items": []}.

Three checks decide whether code is accepted when you save it:

  • The literal text def main(params, user_data, sdk_client): must appear, so annotations, renamed parameters, and different spacing are rejected.
  • Imports must come from the permitted list; any other import is rejected.
  • A security scan rejects dynamic-execution builtins such as eval, exec, compile, open, and __import__, attribute names that start with _, and direct database connections. The response is 400 with Function code failed the security scan and the findings under errors.code. getattr, setattr, and delattr aren't available when the code runs.

See the function signature and sandbox imports for both in full.

Reach the rest of the platform#

Use sdk_client to call TaruviBase from inside the function:

main.py
def main(params, user_data, sdk_client):
invoice = sdk_client.database.get("invoices", params["invoice_id"])
return {"status": invoice["status"]}

The client acts as the caller, or as the function's creator for scheduled runs and anonymous calls to a public function. It uses a system token, so it can also read sensitive secrets the caller couldn't read directly. Don't return secret values to callers.

Handle failures explicitly#

An uncaught exception fails the execution and returns a 400 carrying the exception type and message. That is often the right outcome. Where a caller needs to distinguish failure modes, return them instead:

main.py
def main(params, user_data, sdk_client):
invoice_id = params.get("invoice_id")
if not invoice_id:
return {"ok": False, "error": "invoice_id is required"}

try:
invoice = sdk_client.database.get("invoices", invoice_id)
except Exception as exc:
log("Lookup failed", level="error", data={"error": str(exc)})
return {"ok": False, "error": "invoice not found"}

return {"ok": True, "status": invoice["status"]}

Validate params before use. On a public function it is untrusted input from anyone on the internet.

The log helper above writes a structured entry onto the run's record. See Read logs and invocations.

Save a change#

Saving code creates a new version whenever the code actually changes.

PATCH/api/apps/$TARUVI_APP_SLUG/functions/$FUNCTION_SLUG/

Authorization
Api-Key $TARUVI_API_KEY
Content-Type
application/json
{
"code": "def main(params, user_data, sdk_client):\n invoice_id = params.get(\"invoice_id\")\n if not invoice_id:\n return {\"ok\": False, \"error\": \"invoice_id is required\"}\n log(\"Syncing invoice\", level=\"info\", data={\"invoice_id\": invoice_id})\n return {\"ok\": True, \"invoice_id\": invoice_id}\n"
}
View cURL
curl -X PATCH "$TARUVI_SITE_URL/api/apps/$TARUVI_APP_SLUG/functions/$FUNCTION_SLUG/" \
-H "Authorization: Api-Key $TARUVI_API_KEY" \
-H "Content-Type: application/json" \
--data-binary @- <<'JSON'
{
"code": "def main(params, user_data, sdk_client):\n invoice_id = params.get(\"invoice_id\")\n if not invoice_id:\n return {\"ok\": False, \"error\": \"invoice_id is required\"}\n log(\"Syncing invoice\", level=\"info\", data={\"invoice_id\": invoice_id})\n return {\"ok\": True, \"invoice_id\": invoice_id}\n"
}
JSON

200Returns the updated function. A request that changes no versioned field also returns 200, with status set to error.

Saving without changing anything records no version, and the response reports No changes detected, version unchanged rather than an error status.

Find a function you saved#

List the functions in an app when you need a slug:

client.functions.list()

Read one function to see its current code and version:

client.functions.get("FUNCTION_SLUG")

Review versions and restore one#

Read the history to see what changed and when. Each entry carries a history_id, which is what revert takes.

GET/api/apps/$TARUVI_APP_SLUG/functions/$FUNCTION_SLUG/history/

Authorization
Api-Key $TARUVI_API_KEY
View cURL
curl "$TARUVI_SITE_URL/api/apps/$TARUVI_APP_SLUG/functions/$FUNCTION_SLUG/history/" \
-H "Authorization: Api-Key $TARUVI_API_KEY"

200Returns history records newest first, each with a history_id used by revert.

Revert restores code and filter conditions only

Other versioned fields — including webhook_url, headers, and config — keep their current values, so reverting a function whose configuration changed since that version produces a combination that never existed. Read the history for changes to those fields first. See Versioning.

POST/api/apps/$TARUVI_APP_SLUG/functions/$FUNCTION_SLUG/revert/

Authorization
Api-Key $TARUVI_API_KEY
Content-Type
application/json
{
"history_id": 12
}
View cURL
curl -X POST "$TARUVI_SITE_URL/api/apps/$TARUVI_APP_SLUG/functions/$FUNCTION_SLUG/revert/" \
-H "Authorization: Api-Key $TARUVI_API_KEY" \
-H "Content-Type: application/json" \
--data-binary @- <<'JSON'
{
"history_id": 12
}
JSON

200Restores code and filter_conditions only, and creates a new version rather than rewinding the counter.

Reverting creates a new version rather than rewinding the counter, so the history stays append-only.

Verify#

Execute the function once and confirm it returns what you expect. See Execute a function.

If the save is rejected, the message names the cause — most often the signature check or a compilation error. See Troubleshooting.