> This page is for CLI.

> For clean Markdown of any page, append .md to the page URL.
> For a complete documentation index, see https://developer.deel.com/llms.txt.
> For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://developer.deel.com/_mcp/server.

# Agent workflows

> Patterns for AI agents that discover, call, and recover with the Deel CLI

This page describes how an agent should use the CLI: discover before calling, build bodies from skeletons, read results as data, classify errors, retry safely, follow jobs, and stay within guardrails. The [instruction block](/cli/agents/setup#add-the-instruction-block) encodes these patterns in a form agents follow.

## Discover before calling

The recommended order is: list, read the contract, generate the body, call, read the result.

```bash
deel --help                                             # 1. what exists
deel adjustments create --help                          # 2. which variants
deel adjustments create --invoice --help --json         # 3. the exact contract
deel adjustments create --invoice --generate-input      # 4. a body to fill in
deel adjustments create --invoice --input file://body.json --fields adjustment_id,status   # 5. call and read
```

Steps 1 to 4 run offline and do not need a token or network access. The contract from step 3 tells the agent how to build the body:

| `body.kind`  | The agent should                                                              |
| ------------ | ----------------------------------------------------------------------------- |
| `none`       | Pass parameters as flags only                                                 |
| `json`       | Fill the skeleton object and pass it with `--input`                           |
| `json-array` | Build a JSON array, one item per record, and pass it as a file or inline JSON |
| `multipart`  | Pass text fields with `--input` and files with `--file field=@path`           |

## Build request bodies from skeletons

`--generate-input` prints every field with a typed placeholder. An agent should write the skeleton to a file, replace the values it knows, remove optional fields it does not need, and pass the file back.

```bash
deel adjustments create --invoice --generate-input > body.json
# ... the agent edits body.json ...
deel adjustments create --invoice --input file://body.json
```

Values are validated before the request is sent. A wrong enumeration value or a non-numeric amount fails locally with `input.enum` or `input.type`, without a round trip to the API.

## Read results as data

Ask for only what the next step needs.

```bash
deel adjustments create --invoice --input file://body.json --jq '.adjustment_id' --raw   # one bare value
deel jobs list --fields job_id,status                                                    # a projected envelope
deel jobs status --job_id="$JOB_ID" --jq '.items[] | select(.status == "FAILED")'                  # only the failures
```

`--quiet` prints identifiers only, one per line, which is the form to pipe into the next command.

## Classify errors

On failure the exit status is 1 and stderr carries a JSON envelope. The `code` prefix tells the agent what kind of problem it is and whether it can fix it alone.

| `error.code`                     | Meaning                                                      | Agent action                                            |
| -------------------------------- | ------------------------------------------------------------ | ------------------------------------------------------- |
| `input.*`, `usage.*`             | The command line or body is wrong                            | Fix locally using `--help --json`, then retry           |
| `auth.*`                         | No token, invalid token, or no keychain                      | Stop and report; a person must provide or fix the token |
| `http.400`, API validation codes | The API rejected the body; `message` lists the fields        | Fix the listed fields, then retry                       |
| `http.401`, `http.403`           | The token lacks access                                       | Stop and report; a scope or policy decision is needed   |
| `http.404`                       | The referenced record does not exist                         | Re-check identifiers; do not retry blindly              |
| `http.429`, `http.5xx`           | Rate limited or upstream failure after the CLI's own retries | Wait, then retry the same command                       |
| `network.*`                      | Transport, TLS, or timeout                                   | Report the environment problem                          |

```json
{ "error": { "code": "input.enum", "message": "--type must be one of: BONUS, COMMISSION, DEDUCTION, EXPENSE, OTHER, OVERTIME, TIME_OFF, VAT" } }
```

Every API error includes `request_id`. An agent should quote it when it reports a failure so that support can locate the request. See [Errors and exit codes](/cli/usage/errors) for the full list.

## Retry safely

Every mutating command carries an idempotency key derived from its content. Re-running the identical command after a timeout or an ambiguous failure sends the same key, and the API returns the original result instead of creating a second record. An agent does not need to check whether a write completed successfully before retrying it.

The CLI already retries `429` and `5xx` responses up to three times. An agent that still sees `http.429` should wait longer before its own retry rather than loop immediately. See [Idempotency and retries](/cli/usage/idempotency-and-retries).

## Bulk operations and jobs

Bulk commands return `202` with a `job_id`. The agent should capture the id, poll until a terminal status, and then inspect failed items.

```bash
JOB_ID=$(deel adjustments create-bulk --invoice --input file://batch.json --quiet)
until deel job status "$JOB_ID" --jq '.status' --raw | grep -Eq 'SUCCEEDED|FAILED'; do sleep 5; done
deel jobs status --job_id="$JOB_ID" --jq '.items[] | select(.status == "FAILED")'
```

Batches are limited to 50 items per request; the CLI rejects longer arrays with `usage.max_items` before sending. An agent should split larger sets and use `external_id` on each item to correlate results with its own records. See [Asynchronous jobs](/cli/usage/async-jobs).

## Guardrails

* **Least privilege.** Give the agent a token with only the scopes its tasks need. A `403` is a signal to ask a person, not to try another route.
* **Demo by default.** Keep `DEEL_ENV=demo` in the agent's environment. Production runs are an explicit change made by a person.
* **Review before bulk writes.** Have the agent show the batch file before running `create-bulk`.
* **No token handling.** The agent runs `deel` commands; it never reads, prints, or moves the token. `deel auth status` masks it to its last four characters.
* **Keep logging on.** The local log is the record of what the agent did.

## Auditing an agent session

The CLI writes one newline-delimited JSON (NDJSON) line per request to its [local log](/cli/usage/logging#log-location): timestamp, command, method, URL path, status, duration, request id, and idempotency key, with the token masked and the body omitted. To review what an agent did:

```bash
# macOS
jq -r '[.ts, .command, .method, .status, .request_id] | @tsv' ~/Library/Logs/deel/deel.log

# Linux
jq -r '[.ts, .command, .method, .status, .request_id] | @tsv' ~/.local/state/deel/logs/deel.log
```

Set `DEEL_LOG_DIR` in hosted environments to a directory you collect, or upload the log as a CI artifact. Response bodies are never logged unless `--log-bodies` is passed, so the log can be kept without special handling.

## Next steps

#### [Set up a coding agent](/cli/agents/setup)

Install, token, environment, and the instruction block

#### [Scripting and CI](/cli/guides/automation)

The same patterns for unattended pipelines