> ## Documentation Index
> Fetch the complete documentation index at: https://rubie.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# Triggering Blueprint Runs

> How inputs are routed, how to send files, and how to retrieve completed run results.

A Blueprint is a workflow you and Rubie configure together: which system to log
into, what to extract or write, how to map and validate the data. The API around
it is deliberately thin — you hand Rubie a credential and some inputs, and get
back a run to poll.

Because the workflow lives in configuration rather than in this API, the request
body is **defined by your Blueprint**, not by a fixed schema. This guide explains
the routing rules that make that work.

## Triggering a run

```bash theme={null}
curl -s "$RUBIE_API_URL/api/v1/blueprints/$BLUEPRINT_KEY/trigger" \
  -X POST \
  -H "Authorization: Bearer $RUBIE_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: sync-acme-2026-07-31" \
  -d '{
    "credential_id": "cred_...",
    "run_name": "Nightly sync — Acme",
    "records": [{ "external_id": "1042", "name": "Acme Corp" }],
    "source_system": "acme-prod"
  }'
```

```json theme={null}
{
  "id": "run_...",
  "run_key": "550e8400-e29b-41d4-a716-446655440000",
  "status": "queued"
}
```

`202` means queued, not done. The run executes asynchronously — see
[Polling](#polling-a-run) below.

## How top-level keys are routed

Only two keys are reserved. Everything else is matched by name against your
Blueprint's configuration:

| Key                                | Routed to                                         |
| ---------------------------------- | ------------------------------------------------- |
| `credential_id`                    | The vaulted credential the run authenticates with |
| `run_name`                         | Display name for the run in the Rubie dashboard   |
| Matches an INPUT node's `refKey`   | That node's input payload                         |
| Matches a configured metadata item | Run metadata, coerced to a string                 |
| Matches nothing                    | Ignored                                           |

That last row is the one to watch: **a misspelled input key is silently
dropped**, not rejected. If a run behaves as though a node received no data,
check the key against the Blueprint's `refKey` first.

<Info>
  Your Rubie contact can give you the Blueprint's INPUT `refKey`s and its
  configured metadata items, or you can read them off the Blueprint canvas in
  the dashboard.
</Info>

### Inputs

In the example above, `records` is the `refKey` of an INPUT node. Objects and
arrays are serialized to JSON for the node; strings are passed through untouched.

### Metadata

`source_system` in the example is a metadata item — useful for stamping a run
with correlation ids from your own system (a tenant id, a job id, the record that
triggered the run) so your team can trace it later from the dashboard.

Metadata items must be configured on the Blueprint to be persisted. If one is
marked **required** and you omit it, the trigger returns `400`.

### Credentials

`credential_id` accepts a single id or a comma-separated list, and
`credential_ids` is an accepted alias. Most Blueprints need exactly one; pass
several when a workflow touches more than one system.

The credential must belong to the same account as the API key, and the API key
must be authorized for the Blueprint.

## Sending files

When an INPUT node expects a file rather than inline data, send
`multipart/form-data` instead. The routing rules are identical — the part name is
the key.

```bash theme={null}
curl -s "$RUBIE_API_URL/api/v1/blueprints/$BLUEPRINT_KEY/trigger" \
  -X POST \
  -H "Authorization: Bearer $RUBIE_API_KEY" \
  -H "Idempotency-Key: import-acme-2026-07-31" \
  -F "credential_id=cred_..." \
  -F "run_name=Acme import" \
  -F "records=@customers.csv"
```

Parts matching an input `refKey` may carry either a file or an inline string, so
you can mix uploaded files and inline JSON in the same request.

## Polling a run

Poll `GET /blueprint-runs/{runId}/status` every 2–5 seconds. The endpoint accepts
either the opaque `run_...` id or the UUID `run_key` from the trigger response.

```bash theme={null}
curl -s "$RUBIE_API_URL/api/v1/blueprint-runs/run_.../status" \
  -H "Authorization: Bearer $RUBIE_API_KEY"
```

```json theme={null}
{
  "id": "run_...",
  "run_key": "550e8400-e29b-41d4-a716-446655440000",
  "status": "running",
  "progress_step": "running",
  "created_at": "2026-07-31T00:00:00.000Z",
  "started_at": "2026-07-31T00:00:04.000Z",
  "ended_at": null,
  "error": null,
  "blueprint_id": "bp_..."
}
```

### Status and progress step

`status` is the coarse lifecycle state you branch on. `progress_step` is a finer
projection of the same run, useful for showing the user what's happening.

| `status`    | Terminal? | `progress_step` values       |
| ----------- | --------- | ---------------------------- |
| `queued`    | No        | `queued`, `authenticating`   |
| `running`   | No        | `running`, `awaiting_review` |
| `completed` | Yes       | `completed`                  |
| `failed`    | Yes       | `failed`                     |
| `cancelled` | Yes       | `cancelled`                  |

`authenticating` means the run is logging into the target system, which is where
invalid credentials surface. `awaiting_review` means a human review gate in the
Blueprint is holding the run — it's still `running` from your side, and will
proceed once someone acts on it in the dashboard.

<Warning>
  Treat both fields as open string sets. Finer, Blueprint-defined milestones
  will be added to `progress_step` later. Branch on the values you know and fall
  through to a neutral "in progress" state for anything else.
</Warning>

### This endpoint is a projection, not the run

The status response deliberately contains no run inputs, outputs, file URLs, or
raw error strings — only lifecycle facts. That makes it safe to poll from
systems handling regulated or sensitive data, since nothing the run touched can
leak through it.

Retrieve outputs separately after the run reaches `completed`; see
[Fetching results](#fetching-results).

## Fetching results

Call `GET /blueprint-runs/{runId}/results` only after status is `completed`. Like
the status endpoint, it accepts either the opaque `run_...` id or the UUID
`run_key` from the trigger response.

To return records directly in JSON:

```bash theme={null}
curl -s "$RUBIE_API_URL/api/v1/blueprint-runs/run_.../results?deliveryMethod=direct" \
  -H "Authorization: Bearer $RUBIE_API_KEY"
```

```json theme={null}
{
  "dataDeliveryMethod": "direct",
  "results": {
    "customers": {
      "totalRecords": 1,
      "records": [
        {
          "data": { "external_id": "1042", "name": "Acme Corp" },
          "errors": [],
          "isValid": true
        }
      ]
    }
  },
  "executionMetadata": {
    "runKey": "550e8400-e29b-41d4-a716-446655440000",
    "metadata": { "source_system": "acme-prod" },
    "timings": {
      "executionBeganAt": "2026-07-31T00:00:04.000Z",
      "executionCompletedAt": "2026-07-31T00:02:00.000Z"
    }
  }
}
```

`results` is keyed by Blueprint output node reference key. A node with multiple
output handles uses keys such as `node_ref.handle_key`. Each direct record
contains its output `data`, validation `errors`, and `isValid` flag.

For large datasets, omit the query parameter or use
`deliveryMethod=presigned_url`:

```json theme={null}
{
  "dataDeliveryMethod": "presigned_url",
  "results": {
    "customers": "https://temporary-download-url..."
  },
  "executionMetadata": { "runKey": "...", "metadata": {}, "timings": {} }
}
```

The URLs contain Brotli-compressed JSON, expire after two hours, and should be
downloaded promptly. Direct delivery automatically falls back to presigned URLs
when the estimated response is 5 MB or larger. Always inspect the returned
`dataDeliveryMethod` rather than assuming it matches the request.

The endpoint returns `409` while a run is incomplete and `400` after its data
has been purged.

### Failures

When `status` is `failed`, `error` is populated:

```json theme={null}
{
  "code": "execution_failed",
  "message": "The run failed. Retry the request, or contact support if the issue persists."
}
```

Today every failed run reports `execution_failed`. Specific causes — expired
credentials, a missing template in the target system, a validation failure — are
not yet distinguishable through this endpoint; your Rubie team can inspect the
run in the dashboard. Surface a retry action plus a path back to your reconnect
flow, and treat `error.code` as an open string set so Blueprint-defined codes can
be added later without breaking your client. See [Errors](/guides/errors).

## Idempotency

Trigger requests should always carry an `Idempotency-Key`, keyed on the thing
that caused the run — a record id, a job id, a date-scoped sync key. A retried
request with the same key and body replays the original `202` instead of starting
a second run. Same key with a different body returns `409`. See
[Idempotency](/guides/idempotency).

## Next

* [Hosted credential collection](/guides/hosted-credential-collection) — where `cred_...` comes from
* [Errors](/guides/errors) — the error envelope and validation codes
* [Get Blueprint run results](/api-reference/blueprint-runs/get-blueprint-run-results) — response schema and delivery modes
* [Identifiers](/guides/identifiers) — `run_` ids versus `run_key`
