# Workflow tools

> The curated MCP tool set for the compliance loop: what each tool does, which are operations and which compose them, and the loop they make.

The `workflows` tool set is for agents that work better with a few larger tools. With it alone an agent can run the whole compliance loop: see what needs doing, take the most urgent task, read its packet or package and playbook, do the work in the authority's portal, report back with evidence, and follow the approval.

Most workflow tools are registry operations, so the REST API has them too, with the same arguments. Four are MCP-only compositions: they call a few registry operations in a row with your credentials, each with its own permission, risk tier, approvals, idempotency and audit, and decide nothing an operation would decide.

| Tool                             | What it does                         | Kind                                                                                                                                                                       |
| -------------------------------- | ------------------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `entity_compliance_overview`     | Compliance overview                  | operation [`compliance.overview`](https://ownrecord.io/docs/reference/compliance-overview.md), see [Compliance tools](https://ownrecord.io/docs/mcp/compliance.md)         |
| `tasks_next`                     | Claim the next task                  | operation [`tasks.claim_next`](https://ownrecord.io/docs/reference/tasks-claim-next.md), see [Tasks tools](https://ownrecord.io/docs/mcp/tasks.md)                         |
| `registration_get_packet`        | Get a registration packet            | operation [`registrations.packet`](https://ownrecord.io/docs/reference/registrations-packet.md), see [Registrations tools](https://ownrecord.io/docs/mcp/registrations.md) |
| `filing_prepare_and_get_package` | Prepare a return and get its package | MCP composition of `filing_periods.get`, `filing_periods.lock`, `filing_periods.prepare`, `filing_periods.package` (below)                                                 |
| `task_complete`                  | Complete or submit a task            | MCP composition of `tasks.get`, `tasks.submit`, `tasks.complete` (below)                                                                                                   |
| `approval_status`                | Approval status                      | MCP composition of `approvals.get` (below)                                                                                                                                 |
| `explain_tax_decision`           | Explain a tax decision               | operation [`tax.quotes.explain`](https://ownrecord.io/docs/reference/tax-quotes-explain.md), see [Tax tools](https://ownrecord.io/docs/mcp/tax.md)                         |
| `subscription_change_preview`    | Preview a plan change                | MCP composition of `subscriptions.preview_change` (below)                                                                                                                  |
| `tasks_documents_upload`         | Upload a task document               | operation [`tasks.documents.upload`](https://ownrecord.io/docs/reference/tasks-documents-upload.md), see [Tasks tools](https://ownrecord.io/docs/mcp/tasks.md)             |
| `tasks_fail`                     | Fail a compliance task               | operation [`tasks.fail`](https://ownrecord.io/docs/reference/tasks-fail.md), see [Tasks tools](https://ownrecord.io/docs/mcp/tasks.md)                                     |
| `tasks_heartbeat`                | Extend a task's lease                | operation [`tasks.heartbeat`](https://ownrecord.io/docs/reference/tasks-heartbeat.md), see [Tasks tools](https://ownrecord.io/docs/mcp/tasks.md)                           |
| `tasks_release`                  | Release a compliance task            | operation [`tasks.release`](https://ownrecord.io/docs/reference/tasks-release.md), see [Tasks tools](https://ownrecord.io/docs/mcp/tasks.md)                               |

## The loop

1. `entity_compliance_overview`: registrations that need attention, the next deadlines, unfinished tasks and returns due.
2. `tasks_next`: claim the most urgent task you can finish (a 15-minute lease; keep it with `tasks_heartbeat`). Its result links the task's packet or package and the regime's playbook as resources.
3. For a registration, `registration_get_packet`; for a return, `filing_prepare_and_get_package` (locks and prepares the period, which may need approval, then returns the package and links the report files).
4. Do the work in the authority's portal, under an authorization valid for the regime. Upload the portal's confirmation with `tasks_documents_upload`.
5. `task_complete` with the reference, the receipt and the authorization: dry-run it first. Submissions are compliance tier and usually answer `approval_required`.
6. `approval_status` until it is `executed`; the registration or filing period then changes, with your evidence attached.
7. If you can't finish: `tasks_release`, or `tasks_fail` with a reason and a retry date.

`explain_tax_decision` explains a tax quote in plain words; `subscription_change_preview` shows what a plan change would cost before anyone makes it.

## `approval_status`

**Approval status.** Workflow tool (MCP only) · calls [`approvals.get`](https://ownrecord.io/docs/reference/approvals-get.md) · tool set `workflows`

The status of an approval request you got from an `approval_required` answer (`approvals.get`): `pending`,
`approved` (running), `executed` (done exactly as you sent it), `rejected`, `expired` or `stale` (approved,
but it could no longer run). `finished` is true once nothing more will happen; `remediation` says what to do
next. The full request (its input and dry-run result) is in `approvals_get`.

Annotations: `readOnlyHint: true` · `destructiveHint: false` · `idempotentHint: true` · `openWorldHint: false`

### Arguments

| Argument | Type         | Required | Description                     |
| -------- | ------------ | -------- | ------------------------------- |
| `id`     | ID (`apr_…`) | yes      | The approval request (`apr_…`). |

- **Read-only:** changes nothing.

### Example

**Approved and done**

Arguments:

```json
{
  "id": "apr_01m4ttwd3veh9apbewxheq5xa1"
}
```

Result `output`:

```json
{
  "object": "approval_status",
  "id": "apr_01m4ttwd3veh9apbewxheq5xa1",
  "status": "executed",
  "finished": true,
  "operationId": "tasks.submit",
  "summary": "Submit the task File the One-Stop Shop, Union scheme 2026-Q3 return",
  "approvals": 1,
  "requiredApprovals": 1,
  "rejectionReason": null,
  "failure": null,
  "resultRef": "task_01m4ttwd3veh9apbewxheq5xa9",
  "approveUrl": "https://ownrecord.io/console/acme/approvals/apr_01m4ttwd3veh9apbewxheq5xa1",
  "expiresAt": "2026-11-11T09:10:00.000Z",
  "executedAt": "2026-11-08T10:02:00.000Z",
  "remediation": "Approved and done: the call ran exactly as you sent it. resultRef names what it created or changed."
}
```

## `filing_prepare_and_get_package`

**Prepare a return and get its package.** Workflow tool (MCP only) · calls [`filing_periods.get`](https://ownrecord.io/docs/reference/filing-periods-get.md), [`filing_periods.lock`](https://ownrecord.io/docs/reference/filing-periods-lock.md), [`filing_periods.prepare`](https://ownrecord.io/docs/reference/filing-periods-prepare.md), [`filing_periods.package`](https://ownrecord.io/docs/reference/filing-periods-package.md) · tool set `workflows`

Takes a filing period as far as it can go towards filing, then returns its filing package: it locks an
`open` period (`filing_periods.lock`, compliance tier: under your approval policy this answers
`approval_required`), prepares a `locked` one (`filing_periods.prepare`: the report is generated from the
ledger and invoices and must reconcile with the ledger), and once the period is prepared (or later) reads
`filing_periods.package`. Steps already done are skipped, so call it again after an approval with the same
`idempotency_key` and it continues. `steps` says what each step did.

The result links the package and the report files as resources (`package.json`, `report.csv`,
`report.xlsx`) and the regime's filing playbook: read them there rather than asking for more tool output. A
`dry_run` stops after the first step that would change the period (the next one needs it done). When the
period is prepared, its `filing.prepare` task finishes by itself.

Annotations: `readOnlyHint: false` · `destructiveHint: false` · `idempotentHint: true` · `openWorldHint: false`

### Arguments

| Argument          | Type         | Required | Description                                                                                                                                                                                                                                      |
| ----------------- | ------------ | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `id`              | ID (`fp_…`)  | yes      | The filing period (`fp_…`).                                                                                                                                                                                                                      |
| `entity_id`       | ID (`ent_…`) | no       | The legal entity to act on (ent\_…), when your credentials cover more than one.                                                                                                                                                                  |
| `idempotency_key` | string       | no       | A unique string for this call. Retrying with the same key and arguments never acts twice and continues where the last call stopped. When left out, the server generates one and returns it in the result's `idempotency_key`. (1–200 characters) |
| `dry_run`         | boolean      | no       | true: check permissions and rules and compute the first change's result, then roll it back. Nothing is saved. (default `false`)                                                                                                                  |

- **Legal entity:** pass `entity_id` with an organization credential.
- **Idempotency:** the result returns the `idempotency_key` used. A composition with several changes derives one key per step from it (`<key>:lock`, `<key>:prepare`), so a retry with the same key replays the steps already done and continues where it stopped.
- **Dry run:** supported, for the first step that would change something.

### Example

**A locked period: prepared, then its package**

Arguments:

```json
{
  "id": "fp_01m4ttwd3veh9apbewxheq5xa7"
}
```

Result `output`:

```json
{
  "object": "filing_preparation",
  "steps": [
    {
      "operation": "filing_periods.lock",
      "outcome": "not_needed"
    },
    {
      "operation": "filing_periods.prepare",
      "outcome": "done"
    }
  ],
  "filingPeriod": {
    "id": "fp_01m4ttwd3veh9apbewxheq5xa7",
    "status": "prepared"
  },
  "package": {
    "object": "filing_package",
    "filingPeriodId": "fp_01m4ttwd3veh9apbewxheq5xa7"
  }
}
```

## `subscription_change_preview`

**Preview a plan change.** Workflow tool (MCP only) · calls [`subscriptions.preview_change`](https://ownrecord.io/docs/reference/subscriptions-preview-change.md) · tool set `workflows`

Shows exactly what a plan or quantity change of a subscription would do, without changing anything
(`subscriptions.preview_change`): upgrade or downgrade, when it applies, the invoice it would issue now
(prorations, discounts, tax at today's rates, credit balance applied, amount due) or the scheduled change, and
the monthly recurring amount before and after. Send the same arguments to `subscriptions_update` to make the
change (financial tier: dry-run it, and expect an approval above your limits).

Annotations: `readOnlyHint: true` · `destructiveHint: false` · `idempotentHint: true` · `openWorldHint: false`

### Arguments

| Argument            | Type                                                 | Required | Description                                                                                                                                                                                           |
| ------------------- | ---------------------------------------------------- | -------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `id`                | ID (`sub_…`)                                         | yes      | –                                                                                                                                                                                                     |
| `items`             | array of object                                      | no       | Item changes: `{ id, priceId }` swaps a price, `{ id, quantity }` changes seats, `{ priceId, quantity }` adds an item, `{ id, deleted: true }` removes one. Items not named stay. (1–40 items)        |
| `prorationBehavior` | one of `create_prorations`, `always_invoice`, `none` | no       | `create_prorations` (credit unused time and charge the remaining time on the next invoice; a net credit goes to the customer credit balance), `always_invoice` (invoice the proration now) or `none`. |
| `effective`         | one of `now`, `period_end`                           | no       | `now`, or at the end of the current period (`period_end`, stored as `pendingUpdate`). Default: upgrades and interval changes now with `always_invoice`, downgrades at the period end.                 |
| `paymentBehavior`   | one of `allow_incomplete`, `pending_if_incomplete`   | no       | For a change invoiced now: `allow_incomplete` (default; the change applies and a failed payment goes to dunning) or `pending_if_incomplete` (the old items stay until the invoice is paid).           |
| `couponId`          | ID (`cpn_…`)                                         | no       | Apply a coupon to the subscription.                                                                                                                                                                   |
| `promotionCode`     | string                                               | no       | A promotion code as the customer typed it (case doesn't matter). (1–50 characters)                                                                                                                    |
| `entity_id`         | ID (`ent_…`)                                         | no       | The legal entity to act on (ent\_…), when your credentials cover more than one.                                                                                                                       |
| `brand_id`          | ID (`brd_…`)                                         | no       | The brand to act on (brd\_…), when your credentials cover more than one.                                                                                                                              |

- **Brand:** pass `brand_id` with an organization or entity credential.
- **Read-only:** changes nothing.

### Example

**What upgrading to Pro now costs**

Arguments:

```json
{
  "id": "sub_01m4tx5nj8emwbyf7y9vcf47z5",
  "items": [
    {
      "id": "si_01m4tx5nj8emwbyf7y9vcf47za",
      "priceId": "price_01m4tx5nj8emwbyf7y9vcf47y3"
    }
  ]
}
```

Result `output`:

```json
{
  "object": "subscription_change_preview",
  "subscriptionId": "sub_01m4tx5nj8emwbyf7y9vcf47z5",
  "direction": "upgrade",
  "intervalChange": false,
  "effective": "now",
  "prorationBehavior": "always_invoice",
  "settlement": "invoice_now",
  "invoice": {
    "id": "inv_01m4tx5nj8emwbyf7y9vcf47yk",
    "object": "invoice",
    "livemode": false,
    "entityId": "ent_01m4tx5nj8emwbyf7y9vcf47x2",
    "brandId": "brd_01m4tx5nj8emwbyf7y9vcf47x4",
    "number": null,
    "numbering": null,
    "customerId": "cus_01m4tx5nj8emwbyf7y9vcf47ym",
    "subscriptionId": null,
    "collectionMethod": "send_invoice",
    "kind": "invoice",
    "status": "open",
    "language": "en",
    "issuedAt": "2026-10-20T09:00:00.000Z",
    "issueDate": "2026-10-20",
    "taxPoint": "2026-10-20T09:00:00.000Z",
    "taxPointDate": "2026-10-20",
    "daysUntilDue": 14,
    "dueDate": "2026-11-03",
    "seller": {
      "entityId": "ent_01m4tx5nj8emwbyf7y9vcf47x2",
      "legalName": "Acme Digital d.o.o.",
      "tradingName": null,
      "address": {
        "line1": "Ilica 1",
        "line2": null,
        "city": "Zagreb",
        "postalCode": "10000",
        "region": null,
        "country": "HR"
      },
      "companyRegistryId": "080000001",
      "taxIds": [
        {
          "type": "eu_vat",
          "value": "HR12345678901",
          "regimeId": null
        }
      ],
      "brand": {
        "brandId": "brd_01m4tx5nj8emwbyf7y9vcf47x4",
        "name": "Genno",
        "primaryColor": "#0E6B5C",
        "accentColor": null,
        "supportEmail": "help@genno.example",
        "supportUrl": null
      },
      "footerText": null
    },
    "buyer": {
      "type": "b2b",
      "customerId": "cus_01m4tx5nj8emwbyf7y9vcf47ym",
      "name": "Alpen Apps GmbH",
      "email": "billing@alpen.example",
      "address": null,
      "country": "AT",
      "taxId": {
        "type": "eu_vat",
        "value": "ATU00000000",
        "country": "AT"
      }
    },
    "currency": "EUR",
    "lines": [
      {
        "id": "1",
        "description": "Implementation workshop (days)",
        "productId": "prod_01m4tx5nj8emwbyf7y9vcf47yr",
        "priceId": null,
        "quantity": 2,
        "taxCategoryId": "consulting_remote",
        "taxBehavior": "exclusive",
        "unitAmount": {
          "amount": "600000000",
          "currency": "EUR"
        },
        "net": {
          "amount": "1200000000",
          "currency": "EUR"
        },
        "tax": {
          "ratePpm": "0",
          "amount": {
            "amount": "0",
            "currency": "EUR"
          },
          "jurisdictionId": "AT",
          "regimeId": "EU_OSS_UNION",
          "decision": "reverse_charge",
          "rateKind": null,
          "rateId": null,
          "pendingRegistration": false
        },
        "gross": {
          "amount": "1200000000",
          "currency": "EUR"
        },
        "kind": "invoice_item",
        "period": null,
        "subscriptionItemId": null,
        "invoiceItemId": "ii_01m4tx5nj8emwbyf7y9vcf47ys",
        "discount": null,
        "meteredQuantity": null
      }
    ],
    "totals": {
      "net": {
        "amount": "1200000000",
        "currency": "EUR"
      },
      "tax": {
        "amount": "0",
        "currency": "EUR"
      },
      "gross": {
        "amount": "1200000000",
        "currency": "EUR"
      },
      "taxByRate": [
        {
          "ratePpm": "0",
          "jurisdictionId": "AT",
          "regimeId": "EU_OSS_UNION",
          "decision": "reverse_charge",
          "net": {
            "amount": "1200000000",
            "currency": "EUR"
          },
          "tax": {
            "amount": "0",
            "currency": "EUR"
          }
        }
      ]
    },
    "taxCurrencyTotals": null,
    "creditApplied": {
      "amount": "200000000",
      "currency": "EUR"
    },
    "amountDue": {
      "amount": "1000000000",
      "currency": "EUR"
    },
    "notes": [
      {
        "code": "reverse_charge",
        "text": "Prijenos porezne obveze (reverse charge) – čl. 17. st. 1. Zakona o PDV-u"
      }
    ],
    "fieldCheck": {
      "invoicingRuleIds": [],
      "missing": ["buyer_address"],
      "unchecked": []
    },
    "pendingRegistration": false,
    "paymentIds": [],
    "creditNoteIds": [],
    "ledgerTxnIds": ["txn_01m4tx5nj8emwbyf7y9vcf47yn"],
    "quoteId": "tq_01m4tx5nj8emwbyf7y9vcf47xr",
    "quoteIds": ["tq_01m4tx5nj8emwbyf7y9vcf47xr"],
    "pdf": {
      "status": "pending",
      "fileId": null,
      "sha256": null,
      "size": null,
      "templateVersion": null,
      "renderedAt": null,
      "url": "/api/v1/invoices/inv_01m4tx5nj8emwbyf7y9vcf47yk/pdf"
    },
    "finalizedAt": "2026-10-20T09:00:00.000Z",
    "paidAt": null,
    "payment": null,
    "voidedAt": null,
    "markedUncollectibleAt": null,
    "emailedAt": null,
    "sentAt": null,
    "draft": null,
    "metadata": {
      "contract": "ENT-2026-07"
    },
    "version": 2,
    "createdAt": "2026-10-20T08:30:00.000Z",
    "updatedAt": "2026-10-20T09:00:00.000Z"
  },
  "pendingItems": [],
  "credit": null,
  "subscription": {
    "id": "sub_01m4tx5nj8emwbyf7y9vcf47z5",
    "object": "subscription",
    "livemode": false,
    "entityId": "ent_01m4tx5nj8emwbyf7y9vcf47zm",
    "brandId": "brd_01m4tx5nj8emwbyf7y9vcf47x4",
    "customerId": "cus_01m4tx5nj8emwbyf7y9vcf47z4",
    "status": "active",
    "items": [
      {
        "id": "si_01m4tx5nj8emwbyf7y9vcf47za",
        "priceId": "price_01m4tx5nj8emwbyf7y9vcf47y3",
        "productId": "prod_01m4tx5nj8emwbyf7y9vcf47y0",
        "quantity": 3,
        "createdAt": "2026-10-13T12:00:00.000Z"
      }
    ],
    "currency": "EUR",
    "mrr": {
      "amount": "147000000",
      "currency": "EUR"
    },
    "collectionMethod": "charge_automatically",
    "daysUntilDue": null,
    "defaultPaymentMethodId": "pm_01m4tx5nj8emwbyf7y9vcf47zd",
    "billingCycleAnchor": "2026-10-13T12:00:00.000Z",
    "currentPeriodStart": "2026-10-13T12:00:00.000Z",
    "currentPeriodEnd": "2026-11-13T12:00:00.000Z",
    "trialStart": null,
    "trialEnd": null,
    "trialEndBehavior": "convert",
    "cancelAtPeriodEnd": false,
    "cancelAt": null,
    "canceledAt": null,
    "endedAt": null,
    "cancellation": null,
    "pause": null,
    "discountIds": [],
    "pendingUpdate": null,
    "latestInvoiceId": "inv_01m4tx5nj8emwbyf7y9vcf47zn",
    "incompleteExpiresAt": null,
    "testClockId": null,
    "dunning": null,
    "usageThreshold": null,
    "entitlementGraceUntil": null,
    "metadata": {
      "crm_account": "A-1042"
    },
    "version": 2,
    "createdAt": "2026-10-13T12:00:00.000Z",
    "updatedAt": "2026-10-13T12:00:00.000Z"
  },
  "mrrBefore": {
    "amount": "87000000",
    "currency": "EUR"
  },
  "mrrAfter": {
    "amount": "147000000",
    "currency": "EUR"
  },
  "mrrDelta": {
    "amount": "60000000",
    "currency": "EUR"
  }
}
```

## `task_complete`

**Complete or submit a task.** Workflow tool (MCP only) · calls [`tasks.get`](https://ownrecord.io/docs/reference/tasks-get.md), [`tasks.submit`](https://ownrecord.io/docs/reference/tasks-submit.md), [`tasks.complete`](https://ownrecord.io/docs/reference/tasks-complete.md) · tool set `workflows`

Finishes a compliance task the way its type needs (its `completeWith`): `tasks.submit` for the types whose
completion changes a registration or a filing period (`filing.submit`, `filing.pay`, `registration.submit`,
`registration.confirm`, `deregistration.submit`; compliance tier, so it may answer `approval_required`), else
`tasks.complete` (write tier). It reads the task first (`tasks.get`), then calls that one operation with your
arguments, your `idempotency_key` and `dry_run`.

Submissions take the authority's `reference` (and `effectiveFrom` for `registration.confirm`, `paidOn` for
`filing.pay`), the `authorizationId` you acted under (`*.submit` tasks), and `outputs` (receipt files uploaded
with `tasks_documents_upload`, more references, notes). Work tasks take only `outputs`: sending a reference or
an authorization for one is refused. Dry-run first. When the answer is `approval_required`, nothing changed
yet: follow it with `approval_status`.

Annotations: `readOnlyHint: false` · `destructiveHint: false` · `idempotentHint: true` · `openWorldHint: false`

### Arguments

| Argument          | Type          | Required | Description                                                                                                                                                                                                                                      |
| ----------------- | ------------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `id`              | ID (`task_…`) | yes      | The task (`task_…`).                                                                                                                                                                                                                             |
| `reference`       | string        | no       | Submissions: the authority's reference (the return's, the application's, the payment's). (1–200 characters)                                                                                                                                      |
| `effectiveFrom`   | string        | no       | `registration.confirm`: the first day the registration is effective.                                                                                                                                                                             |
| `paidOn`          | string        | no       | `filing.pay`: the day the tax was paid.                                                                                                                                                                                                          |
| `authorizationId` | ID (`atz_…`)  | no       | `*.submit` tasks: the authorization the work was done under.                                                                                                                                                                                     |
| `outputs`         | object        | no       | Evidence: more references by name, when it was submitted, receipt files, notes.                                                                                                                                                                  |
| `expectedVersion` | integer       | no       | Refuse if the task changed since this version. (1–9007199254740991)                                                                                                                                                                              |
| `entity_id`       | ID (`ent_…`)  | no       | The legal entity to act on (ent\_…), when your credentials cover more than one.                                                                                                                                                                  |
| `idempotency_key` | string        | no       | A unique string for this call. Retrying with the same key and arguments never acts twice and continues where the last call stopped. When left out, the server generates one and returns it in the result's `idempotency_key`. (1–200 characters) |
| `dry_run`         | boolean       | no       | true: check permissions and rules and compute the first change's result, then roll it back. Nothing is saved. (default `false`)                                                                                                                  |

- **Legal entity:** pass `entity_id` with an organization credential.
- **Idempotency:** the result returns the `idempotency_key` used. A composition with several changes derives one key per step from it (`<key>:lock`, `<key>:prepare`), so a retry with the same key replays the steps already done and continues where it stopped.
- **Dry run:** supported, for the first step that would change something.

### Example

**Submit the filed Q3 return, which needs approval**

Arguments:

```json
{
  "id": "task_01m4ttwd3veh9apbewxheq5xa9",
  "reference": "HR/HR12345678901/Q3.2026",
  "authorizationId": "atz_01m4ttwd3veh9apbewxheq5xa3",
  "outputs": {
    "files": ["file_01m4ttwd3veh9apbewxheq5xah"]
  }
}
```

Result `output`:

```json
{
  "object": "task_completion",
  "operation": "tasks.submit",
  "task": {
    "id": "task_01m4ttwd3veh9apbewxheq5xa9",
    "type": "filing.submit",
    "status": "done"
  }
}
```
