Menu

Let your system decide a step

Let a process step ask your own system and wait for its answer: your ERP checks a credit limit, prints a label, or approves a return. Picked calls your endpoint, the work sleeps, and your system calls back to say which way it goes.

When to use this

Use this recipe when a decision belongs to a system Picked doesn't run, and that system can answer in its own time. Each call carries its own callback address, so your system never has to work out which piece of work it is answering.

If your system only needs to send a signal that several waiting processes listen for, resume a process with a signal instead.

Build the step

In the process builder, open the step that should ask:

  1. Set its input to Wait.
  2. Under What ends the wait, choose An answer from your system.
  3. Fill in Your endpoint: the https address Picked calls. Write the host out: it can't come from a variable.
  4. Optional: Headers, such as an Authorization header. Send an API key as {{ secret.name }} after adding it under Settings → Secrets, never as plain text. Headers are sent as written: only {{ secret.name }} is filled in, not {{ variables }}.
  5. Optional: Body, the fields Picked sends. Values can use {{ variables }} from the work, such as {{ order_ref }}.
  6. Give up after (minutes), up to a week (10080 minutes).
  7. Draw an outgoing edge from the step for each outcome your system can send back, such as approved and rejected, plus one plain edge for anything else. timed_out and call_failed can have their own edges too.

Publish the process. Work that reaches the step now calls your endpoint and sleeps there.

An AI agent connected to Picked can build the same step: ask it for an external action step.

What your endpoint receives

A POST with your body fields as JSON, and a picked object beside them:

POST /picked/check HTTP/1.1
Host: erp.example.com
Content-Type: application/json
Authorization: Bearer <your key>

{
  "order": "SO-48211",
  "picked": {
    "callback_url": "https://picked.nl/api/v1/process-runs/callbacks/q7Lx2VnR9bT4wE6yH1mK8sD3fG5jP0cZa1B2c3D4e5F6g7H8i9J0k1L2m3N4o5P6",
    "expires_at": "2026-10-14T09:30:00+00:00",
    "run_id": 5512,
    "step": "ask_erp"
  }
}

Answer the call itself with any 2xx within 10 seconds, and do the work afterwards. A call that times out, or answers 5xx, 408 or 429, is tried three times in all; any other 4xx isn't tried again. If none gets through, the step continues with the outcome call_failed.

Call back

POST to the callback_url, before expires_at. It needs no API token: the address is the credential, and it works for this one piece of work only. Keep it as you would a password.

  • outcome (optional): which edge the work takes, in lowercase letters, digits and _. Without it the outcome is completed. An outcome no edge names takes the step's plain edge.
  • data (optional): any JSON object, up to 100 top-level keys and 64 KB. The work can read it from then on as wait_payload, and the step's outputs take their fields from it: see below.
curl -X POST "https://picked.nl/api/v1/process-runs/callbacks/q7Lx2VnR9bT4wE6yH1mK8sD3fG5jP0cZa1B2c3D4e5F6g7H8i9J0k1L2m3N4o5P6" \
  -H "Accept: application/json" \
  -H "Content-Type: application/json" \
  -d '{
    "outcome": "approved",
    "data": { "credit_limit": 2500, "approved_by": "j.devries" }
  }'
HTTP/1.1 200 OK
Content-Type: application/json

{ "status": "woken" }

Use the answer in later steps

To show or use a field of the answer in later steps, not only branch on it, give the step an output for it. In the builder, under the step's Outputs, add one per field:

  • Name: what later steps call it, such as suggested_bin. Not a name Picked keeps for itself, such as order_id or wait_payload.
  • Type: text, number, true/false, object or list.
  • Field in the answer: where it is in data, with dots for nested fields, such as bin or label.id.
  • Default (optional): the value when the answer leaves the field out, or when the step timed out.

Later steps read it as {{ steps.<step>.suggested_bin }}: in their text, in action settings, in conditions, and in the body of a later call to your system.

A callback whose data lacks a field without a default, or has one of another type, answers 422 naming the field (data.bin), and the work keeps waiting: send it again corrected. JSON types count as sent: "4" is text, not a number.

Read the answer

Status Meaning
200 woken: the work carries on. repeated: this callback already woke it, and a repeat changes nothing. Retrying a callback is always safe.
503 temporarily_unavailable: the work can't carry on yet, for want of stock or capacity. It is still waiting: call again after the Retry-After seconds.
410 gone: the step stopped waiting: it timed out, the call failed, or someone ended the wait. The work has gone its own way, and this address no longer works.
404 not_found: no work waits on this address.
422 The outcome isn't a name an edge could have, or is one Picked sets itself (timed_out, call_failed, force_resumed, released), or data is too large, not an object, or lacks a field the step reads, or has one of another type.

Errors lists every code, and Rate limits what to do on a 429.

Testing

Callbacks can't be dry-run: a Picked-Dry-Run: true header answers 400 here and nothing is done. Test with a process of your own: point its step at an endpoint you control, let a piece of work reach the step, and call the callback_url your endpoint received.