Browse documentation

Automation and CI

Use dynomate-cli requests run in scripts and CI jobs. Ask for JSON, check the exit code, and approve the request in advance with --approval never.

First, install the CLI on the runner.

JSON output

--format json prints one JSON document to standard output, also when the request fails. This is the running example with endpointUrl = "http://localhost:8000", against DynamoDB Local:

Shell
dynomate-cli requests run ./orders/find-and-update-order.dnml --approval never --format json
JSON
{  "version": "1.0",  "request": "find-and-update-order",  "status": "success",  "startedAt": "2026-09-23T06:45:48.976Z",  "finishedAt": "2026-09-23T06:45:49.456Z",  "error": null,  "outcomes": [    {      "name": "Find recent order",      "type": "dynamodb.query",      "status": "success",      "startedAt": "2026-09-23T06:45:48.978Z",      "durationMs": 456,      "destructive": false,      "endpoint": "http://localhost:8000",      "endpointUses": [        {          "operationName": "Find recent order",          "operationKind": "dynamodb.query",          "role": "operation",          "service": "dynamodb",          "profile": "commerce-dev",          "region": "ap-southeast-2",          "endpoint": "http://localhost:8000",          "source": "documentOverride"        }      ],      "result": {        "count": 1,        "items": [          {            "accountId": "account#42",            "orderId": "order#9812",            "status": "PENDING",            "total": 42          }        ],        "lastEvaluatedKey": "eyJvcmRlcklkIjp7IlMiOiJvcmRlciM5ODEyIn0sImFjY291bnRJZCI6eyJTIjoiYWNjb3VudCM0MiJ9fQ==",        "pages": 1,        "requestId": "280d3dd1-c3f2-4cd0-94cd-6ed02fa5d258",        "scannedCount": 1      }    },    {      "name": "Mark reviewed",      "type": "dynamodb.update",      "status": "success",      "startedAt": "2026-09-23T06:45:49.435Z",      "durationMs": 9,      "destructive": true,      "endpoint": "http://localhost:8000",      "endpointUses": [        {          "operationName": "Mark reviewed",          "operationKind": "dynamodb.update",          "role": "operation",          "service": "dynamodb",          "profile": "commerce-dev",          "region": "ap-southeast-2",          "endpoint": "http://localhost:8000",          "source": "documentOverride"        }      ],      "result": {        "attributes": {          "accountId": "account#42",          "orderId": "order#9812",          "status": "REVIEWED",          "total": 42        },        "requestId": "4a75c4cf-47a1-4115-a8d1-85242d4cf39f"      }    },    {      "name": "Read back",      "type": "dynamodb.get",      "status": "success",      "startedAt": "2026-09-23T06:45:49.444Z",      "durationMs": 11,      "destructive": false,      "endpoint": "http://localhost:8000",      "endpointUses": [        {          "operationName": "Read back",          "operationKind": "dynamodb.get",          "role": "operation",          "service": "dynamodb",          "profile": "commerce-dev",          "region": "ap-southeast-2",          "endpoint": "http://localhost:8000",          "source": "documentOverride"        }      ],      "result": {        "found": true,        "item": {          "accountId": "account#42",          "orderId": "order#9812",          "status": "REVIEWED",          "total": 42        },        "requestId": "1ebbbe86-9637-4e7f-8fe2-f0048f0609c4"      }    }  ]}
Lines 1–20 of 107
FieldDescription
versionThe DNML version of the request: "1.0", or "1.1" for a request with an Amazon Aurora DSQL operation.
requestThe request name.
statussuccess, partial or failed.
startedAt, finishedAtUTC times of the run.
errorA request-level error, for example from validation, or null.
outcomesOne outcome for each operation, in run order. It is empty if the run did not start.

Each outcome has the name, type and status of the operation, and a result, error or skipReason. An operation that started also has startedAt and durationMs. It can also have destructive, endpoint and endpointUses. Aurora DSQL outcomes have no endpoint or endpointUses.

The Operation reference shows the result of each operation type.

Parse the result

Read the JSON with a tool such as jq:

Shell
# Overall status: success, partial or failedjq -r '.status' result.json# One line per operation, with the skip reason when there is onejq -r '.outcomes[] | "\(.name): \(.status) \(.skipReason // "")"' result.json# Error code of the request, or of every failed operationjq -r '.error.code // empty, (.outcomes[] | .error.code // empty)' result.json# A value from one operation's resultjq -r '.outcomes[] | select(.name == "Read back") | .result.item.status' result.json

Standard output contains only the result, so you can parse it safely.

Exit codes in CI

Use the exit code to pass or fail a CI step:

  • 0: the request succeeded.
  • 1: the status is partial or failed.
  • 2: the command line is not valid. Do not parse the empty output.

This step prints each operation status and keeps the exit code:

Shell
#!/usr/bin/env bashset -uo pipefaildynomate-cli requests run ./orders/find-and-update-order.dnml \  --approval never \  --format json > result.jsoncode=$?if [ "$code" -eq 2 ]; then  echo "dynomate-cli was called with invalid arguments" >&2  exit 2fijq -r '.outcomes[] | "\(.name): \(.status)"' result.jsonexit "$code"

Approval in automation

A CI job has no terminal. A request that needs approval fails with APPROVAL_FAILED and exit code 1.

With the default --approval auto, a request with dynamodb.deleteTable, dynamodb.truncate or an endpointUrl needs approval. Aurora DSQL operations do not need approval.

To run the request, use --approval never. Do not use --approval always in automation, because each run needs a terminal.

Pass inputs and secrets

Templates cannot read environment variables, so give each value with --input:

Shell
dynomate-cli requests run ./orders/nightly-check.dnml \ --approval never \ --format json \ --input ORDERS_TABLE_ARN="$ORDERS_TABLE_ARN" \ --input ACCOUNT_ID="$ACCOUNT_ID" > result.json

Share app data

The CLI uses the saved profile endpoints, Aurora DSQL network settings and global environments of the app. To use a different directory, set DYNO_APP_DATA_DIR:

Shell
DYNO_APP_DATA_DIR="$HOME/dynomate-data" dynomate-cli requests run --collection ./orders --request find-and-update-order --global-env staging

A CI runner usually has no app data. Write the endpoint in the request, and give values with --input or --collection-env.

Without app data, the CLI connects to each Aurora DSQL cluster through its public endpoint and verifies the certificate with the system trust store. The runner needs outbound TCP port 5432.

DynamoDB Local in CI

To test in CI with no AWS credentials:

  1. Start DynamoDB Local with -sharedDb:
    docker run -d -p 8000:8000 amazon/dynamodb-local -jar DynamoDBLocal.jar -sharedDb -inMemory
  2. Set endpointUrl in [defaults] or on an operation:
    DNML
    [defaults]profileName = "commerce-dev"endpointUrl = "http://localhost:8000"
  3. Create the tables and add seed data.
  4. Run the request with --approval never.
  • Without -sharedDb, DynamoDB Local keeps separate data for each access key and region. Dynomate uses its own placeholder keys, so the request cannot see tables from other tools.
  • Dynomate accepts plain HTTP only for localhost, IPv4 loopback addresses such as 127.0.0.1, and ::1.
  • For localhost, 127.0.0.1 and ::1, Dynomate does not use the AWS credentials of the profile.
  • With -sharedDb, you do not change the table ARNs in the request.

To add seed data with table import, use a profile with the local endpoint_url and placeholder keys.

For a GitHub Actions example, see DynamoDB Local in CI/CD pipelines.