Skip to main content
Plexara
Integration10 min read

706 - From submission to action

Each accepted submission is an event in the form’s source, and an automation can run once for every one. The demo tenant’s intake automation checks each supplier application against the warehouse and posts a summary to an operations feed seconds after it is stored. Validation against live data that refuses field by field before anything is stored, what an event run reads, one run per event, what happens on a burst and on a failure, and where a write to another system belongs.

What you will take away from this lesson

A table of applications is only useful if somebody looks at it. The vendor team wants to hear about each application as it arrives, with the facts that decide whether to call back: which category, how much of it ACME already carries, which store the supplier wants to start at, and who manages it.

Each accepted submission is an event in the form’s source, the same kind of source the 600 series read webhooks from. An automation can name that source and run once for each event it stores.

Learning Objectives

  1. 01Run an automation once for every submission a form stores.
  2. 02Read a submission from run.event and act on it under the automation’s own access.
  3. 03Refuse a submission against live data before it is stored, field by field.
  4. 04Say what happens on a burst of submissions and on a failed run.
  5. 05Decide whether work belongs in form_submit or in the run that follows.

Where this lesson sits

Lessons 703 to 705 built the supplier form, published it, and protected its tax ID. This lesson acts on what it collects.

700 Series: Endpoints and Forms

Open index

The 700 series builds on the automations of the 600 series: an endpoint is an automation that answers a request, and a form is an endpoint that people fill in.

The intake automation

The agent wrote an automation that takes one application, adds what the warehouse knows, and tells the vendor team.

acme-supplier-intake (body shortened)

def intake(event):
    """Checks one supplier application against the warehouse and posts it to the operations feed."""
    values = event["payload"]["values"]
    store = platform.query(
        "SELECT s.store_name, s.city, s.state, s.manager_name, r.region_name FROM warehouse.public.stores s " +
        "JOIN warehouse.public.regions r ON r.region_id = s.region_id WHERE s.store_id = :id",
        params = {"id": int(values["pilot_store"])}, connection = "acme",
    )["rows"][0]
    category = platform.query(
        "SELECT c.category_name, count(p.product_id) AS products FROM warehouse.public.categories c " +
        "LEFT JOIN warehouse.public.products p ON p.category_id = c.category_id " +
        "WHERE c.category_id = :id GROUP BY c.category_name",
        params = {"id": int(values["category"])}, connection = "acme",
    )["rows"][0]
    platform.notify(
        channel = "ops-events",
        title = "Supplier application: " + values["company_name"],
        body = ...,  # company, category and products carried, contact, pilot store and manager,
                     # volume, first delivery, files attached, "Tax ID: received, encrypted"
        data = {"kind": "supplier_application", "event_id": event["id"], ...},
    )

def main():
    """Handles the supplier application this event run was made for."""
    if run.event == None:
        fail("acme-supplier-intake runs once per stored application; start it by submitting the supplier-onboarding form")
    platform.result(intake(run.event))

The automation reads the submission from run.event, looks the pilot store and the category up in the warehouse, and posts a summary to the operations feed with a structured payload a system can route on. It never decrypts the tax ID. Started any other way, it fails with a message saying how it is meant to run.

Running it once per submission

The automation runs for the form’s source once it names it.

Naming the form’s source

What the agent calls. This is the exchange the agent has with Plexara on your behalf, shown for the technical reader. You ask in plain language; you never type any of it.

→ manage_script
  { "command": "update", "name": "acme-supplier-intake",
    "event_source": "form-supplier-onboarding", "exclusive": "queue" }

← { "status": "updated", "version": 1,
    "message": "Saved, and this version is what runs now ..." }

The same setting is on the automation’s page in the portal, under Events, beside the schedule. The owner of a form may name that form’s source; an administrator may name any source. Setting it to queue makes runs one at a time, in the order submissions arrived.

What a run reads

An event run is handed the stored submission. Every other run has no event.

What run.event holds

id

The stored event’s id, the one the run is made once for.

type, key

form_submission, and the submitter’s address on a form that requires sign-in.

received_at

When the source received it.

source

form-supplier-onboarding.

payload

The submission: values (sensitive ones as ciphertext, files described), the fields in order, and the submitter.
An intake automation's run history: the overlapping runs setting with Allowed, Refused and Queued, and six succeeded runs, each labelled event with the version it ran, its duration and one output.
Run history for an automation that runs once per event. Each run is labelled event.

One application, one run

Northfield Paper Goods applied to supply paper products with a pilot at a store in Texas. This is the run its application made.

One application, one run

What the agent calls. This is the exchange the agent has with Plexara on your behalf, shown for the technical reader. You ask in plain language; you never type any of it.

→ manage_script get_run   acme-supplier-intake

← { "trigger": "event",
    "event_source": "form-supplier-onboarding",
    "event_id": "345829bdb1f4a944cf0d3eff46783698",
    "status": "succeeded", "duration_ms": 431, "queries": 2,
    "result": { "company": "Northfield Paper Goods",
                "category": "Paper Products", "region": "Southwest" },
    "log": "notify: send to ops-events: accepted for delivery" }

  what the operations feed received
  title  Supplier application: Northfield Paper Goods
  body   **Northfield Paper Goods** applied to supply **Paper Products**
         (207 products in the category today).
         - Contact: Grace Okafor, [email protected]
         - Pilot store: ACME Store #254, Fairview, TX (Southwest; manager Elizabeth Garcia)
         - Expected annual volume: $350000
         - Certificate of insurance: 1 file attached
         - Tax ID: received, encrypted
  link   the run's page in the portal

The run started seconds after the submission was stored and took under half a second. The post links back to the run, so whoever reads it can open what produced it. On the demo tenant the feed is a webhook channel received by a source of its own; a team would point the same post at a Slack or Mattermost channel or an email list.

The Events section

The automation’s page counts what its source has handed it and what is waiting.

The intake automation’s Events section

applications stored, 37 event runs
37
runs failed
0
pending in the queue
0
pending runs the queue holds before skipping
100
An intake automation's page with its Events section open: an explanation that each event the named webhook source stores runs the script once, the webhook source set to form-supplier-onboarding, Save and Stop running for events, and lines for runs waiting, runs skipped in the last seven days and the last event run.
The Events section names the source the automation runs for and shows what is waiting and what was skipped.

What an event run guarantees

A form that collects applications needs every one acted on once, even when a burst arrives or a run fails.

What an event run guarantees

  1. 1

    One event, one run

    A run is made once per automation, source, and event id. A second server noticing the same submission makes no second run.

  2. 2

    Never inside the person’s request

    The run is queued when the submission is stored and executes afterward, so a slow or failing automation never slows the form or loses a submission.

  3. 3

    A failure alerts and is not replayed

    A failed run is recorded with its cause and its owner is told. The platform does not run it again on its own, and the next submission still runs.

  4. 4

    A burst is bounded

    An automation holds at most 100 pending event runs. A submission past that is recorded as skipped, with a reason saying it is still in the source, and the Events section counts skips for seven days.

  5. 5

    One at a time when asked

    An automation set to run one at a time runs its event runs in the order they arrived; an event run waits rather than being refused.

These are the rules lesson 607 described for sources other systems post to. A form’s source is one more source.

Refusing against live data

Some checks need data, and they belong before the submission is stored, where the person can still correct it.

Version 2 of the form: checked against the stored applications

def already_applied(company):
    """Reports whether an application from this company is already stored."""
    rows = platform.query(
        "SELECT count(*) AS n FROM scratch.uploads.webhook_form_supplier_onboarding " +
        "WHERE lower(trim(json_extract_scalar(payload, '$.values.company_name'))) = lower(trim(:company))",
        params = {"company": company}, connection = "acme",
    )
    return rows["rows"][0]["n"] > 0

def form_submit(req, values):
    errors = {}
    ...
    if already_applied(values["company_name"]):
        errors["company_name"] = "An application from this company is already with the vendor team. " +
                                 "Reply to its confirmation instead of applying again."
    if errors:
        return {"errors": errors}
    return None

The vendor team asked for repeat applications to be turned away. The agent added a query to form_submit, and because that changes what the form does, the save waited for the owner to agree to a plain-language summary of the change. Its tests replay a draft that tried a new company, a malformed tax ID, and a repeat with different capitals and spaces.

On the demo tenant a second application from Lakeshore Pantry Co. was answered 422 with that message on the Company name field. Nothing was stored and no run was made.

Where the work belongs

The supplier program now has work on both sides of the store: a check before it and an action after.

Where the work belongs

form_submit

before storing

Checks that decide whether the submission is acceptable at all, answered to the person on the page: a malformed value, a repeat, a store that is not taking suppliers. Keep them quick; the person is waiting.

the event run

after storing

Everything that acts on an accepted submission: a post to a channel, a lookup that enriches it, a write to the CRM or the ERP through an API connection. A slow or unavailable system delays the run, never the person.

A write to another system from the event run goes through an API connection that holds the credential, so the automation never sees it, and the call is audited under the automation’s name like every other call it makes.

Key terms

Three terms cover how an automation runs for each submission.

Key Terms

Event run
A run made once for one event a source stores, reading it as run.event, under the automation’s saved access.
Event source
The source an automation runs for. A form’s source is form-<endpoint>.
Skipped backlog
The record of an event past an automation’s queue of 100 pending runs. The event stays in its source.