App platform

Automation triggers and actions

Apps can add their own triggers and actions to Kweko automations. A trigger starts automations when something happens in your app; an action is a step that calls your backend and hands its outputs to the next steps.

How it works

PieceWhat it does
TriggerYour backend calls POST /v1/apps/triggers/{key} for a lead, contact or company. Kweko starts every active automation listening to app:<your app slug>:<key> for that record.
ActionA step in an automation. When a run reaches it, Kweko sends a signed POST to your backend; you answer with outputs.
Fields and outputsTyped values. Conditions and texts read a trigger's fields as event.data.<field> and an action's outputs as run.app.<slug>.<action>.<output>.

Both need the automations:extend scope, plus the read scope of the record type they work on (leads:read, contacts:read or companies:read). Workspaces see your triggers and actions in pipeline settings, grouped under Apps with your logo. Adding the scope to a published app asks every workspace to approve it again.

Automations stay bound to a pipeline stage: a trigger starts an automation only for leads that are in its stage, exactly like Kweko's own triggers.

Declare them in the manifest

Edit them in the developer console (Automation tab) or send them with your manifest:

json
{
  "scopes": ["leads:read", "automations:extend"],
  "base_url": "https://shop.example.com",
  "automation": {
    "triggers": [
      {
        "key": "order_paid",
        "entity": "lead",
        "title": { "en": "Order paid", "ru": "Заказ оплачен", "uz": "Buyurtma toʻlandi" },
        "description": { "en": "The shop received the payment." },
        "fields": [
          { "key": "order_id", "type": "string", "title": { "en": "Order number" } },
          { "key": "amount", "type": "number", "title": { "en": "Amount" } },
          { "key": "method", "type": "enum", "values": ["cash", "card", "transfer"], "title": { "en": "Payment method" } }
        ]
      }
    ],
    "actions": [
      {
        "key": "check_stock",
        "entity": "lead",
        "url": "/kweko/automation/check-stock",
        "title": { "en": "Check stock" },
        "params": {
          "type": "object",
          "required": ["sku"],
          "properties": {
            "sku": { "type": "string", "title": "SKU", "maxLength": 60 },
            "warehouse": { "type": "string", "title": "Warehouse", "enum": ["tas", "sam"], "enumNames": ["Tashkent", "Samarkand"] }
          }
        },
        "outputs": [
          { "key": "in_stock", "type": "boolean", "title": { "en": "In stock" } },
          { "key": "quantity", "type": "number", "title": { "en": "Quantity" } }
        ]
      }
    ]
  }
}
RuleLimit
Triggers, actionsUp to 20 of each
key (trigger, action, field, output)1 to 40 lowercase letters, digits or _, starting with a letter; unique in its list
titleRequired, by language, 1 to 60 characters, no markup
descriptionOptional, by language, up to 300 characters
entitylead, contact or company, with its read scope granted
Trigger fieldsUp to 30. type is string, number, boolean, date or enum; an enum lists 1 to 50 values
Action paramsThe settings schema format, up to 30 properties. No secret fields: secrets belong in your app settings
Action outputsUp to 20. type is string, number, boolean or date
Action urlA path on your base_url that starts with a single /

Workspaces fill an action's params with Kweko's own controls, the same ones your settings page uses (pickers for pipelines, stages, members and fields included). Text params may hold templates such as {{lead.name}}.

Fire a trigger

Call it from your backend with the installation's access token:

Request
curl -X POST https://api.kweko.uz/v1/apps/triggers/order_paid \
  -H "Authorization: Bearer kwk_at_…" \
  -H "Idempotency-Key: ORD-1042" \
  -H "Content-Type: application/json" \
  -d '{"lead_id": "ld_01j8za0b1c2d3e4f5g6h7j8k9m", "data": {"order_id": "ORD-1042", "amount": 250000, "method": "card"}}'
Response
{ "trigger": "order_paid", "runs_started": 1, "dropped": [] }
  • Send the record as lead_id, contact_id or company_id, matching the trigger's entity. It must exist in the installation's workspace.
  • A contact or company trigger runs for that record's leads, the 50 most recently updated.
  • data keeps only the fields you declared; unknown keys and null values are dropped and listed in dropped. Each value must match its type: text up to 1000 characters, a number, true or false, a date as YYYY-MM-DD or RFC 3339, or one of the enum's values. data is at most 16 KB.
  • Idempotency-Key (or idempotency_key in the body), 1 to 100 letters, digits, ., _, : or -: the same key for the same record starts each automation once. Use your own event or order id.
  • 202 means accepted; runs_started is how many automations started now. 0 is normal when none listen in the lead's stage.
  • Each installation may fire 5 triggers a second, in bursts of up to 60.
StatusCodeWhen
403app_not_installedThe installation is not active
403app_suspendedKweko disabled the app
403missing_scopeThe workspace has not granted automations:extend or the record's read scope
404unknown_triggerThe installed version of your app has no trigger with this key
422validation_failedA bad record id, a value of the wrong type, data over 16 KB
429rate_limitedToo many triggers; wait retry_after seconds
503automations_unavailableAutomations are paused on Kweko's side; try again later

Answer an action

When a run reaches your action, Kweko sends a POST to base_url + url, signed with your client secret like every other request (see verify signatures):

http
POST /kweko/automation/check-stock
Content-Type: application/json
Kweko-App: app_01j8za0b1c2d3e4f5g6h7j8k9m
Kweko-Timestamp: 1767225600
Kweko-Signature: t=1767225600,v1=5f0c…
Kweko-Installation-Id: ins_01j8…
Kweko-Action-Id: run_01j8…:2
json
{
  "type": "automation.action",
  "action": "check_stock",
  "action_id": "run_01j8…:2",
  "workspace": { "id": "ws_01j8…" },
  "installation_id": "ins_01j8…",
  "automation": { "id": "au_01j8…", "name": "Paid orders" },
  "run_id": "run_01j8…",
  "entity": { "type": "lead", "id": "ld_01j8…", "name": "Order #1042", "amount": 2500, "currency": "UZS" },
  "params": { "sku": "TSHIRT-RED-M", "warehouse": "tas" },
  "locale": "en",
  "issued_at": "2026-01-01T00:00:00Z"
}
  • entity always has type and id; the other fields come only with the record type's read scope.
  • params are what the workspace filled in, with templates already resolved.
  • action_id is the same for every retry of one step in one run. Use it to skip work you already did.

Answer within 10 seconds with JSON:

json
{ "outputs": { "in_stock": true, "quantity": 12 } }

Every output you return must be declared and match its type; leave out the ones you don't have. Later steps read them as {{run.app.<slug>.check_stock.in_stock}}, where <slug> is your app's slug with - turned into _.

With @kweko/sdk/server:

ts
import { verifyActionRequest } from "@kweko/sdk/server"

const req = await verifyActionRequest({ secret: process.env.KWEKO_CLIENT_SECRET!, headers: request.headers, body: raw })
if (!req) return new Response("bad signature", { status: 401 })
return Response.json({ outputs: { in_stock: true, quantity: 12 } })

Retries and failures

What happensThe step
Your answer is 2xx with valid outputsSucceeds; outputs are stored on the run
Timeout, connection error, 429 or 5xxIs retried after 10 seconds, 1 minute and 5 minutes, then fails
Any other 4xxFails at once (app_error)
Invalid JSON or outputs that don't match the manifestFails (app_bad_response)
The app is uninstalled, suspended, lost a scope or no longer has the actionFails with app_not_installed, app_suspended, app_missing_scope or app_unknown_action

Each failure shows in the automation's run log with the reason. The editor flags automations whose app is missing or suspended. Test runs (the editor's Test button) never call your backend.

Events

Separately from automation, an app can receive webhook events (lead.won, task.created and the rest of the event catalog) for every installation: pick them in the console under Extension points, or list them as events in the manifest. They go to your webhook URL, signed with your client secret, and each group needs its read scope.

The example app

packages/example-app in the Kweko repository declares the order_paid trigger and the check_stock action above. Its /demo/order-paid route fires the trigger and /kweko/automation/check-stock answers the action.