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

# Setup

> One-time admin configuration: ingest transactions, define submission requirements, configure approval policies, and set up accounting fields.

## Overview

Setup has two distinct parts, each performed by a different actor:

* **Your integration** pushes transactions into Tesouro — typically from a card feed in real time or in batch. This uses your partner-level credentials.
* **Your customer's admin** configures the organization: what employees must provide before submitting, which transactions require approval and who signs off, and which ledger accounts expenses map to.

Run this once per organization and update it as your customers' policies change.

## Upload transactions

Transactions are the core objects in expense management. Every other step — receipt matching, validation, approval — happens in the context of a transaction.

### Create a transaction

To create a single transaction, call `POST /transactions`:

```sh expandable lines theme={null}
curl -X POST 'https://api.sandbox.tesouro.com/v1/transactions' \
  -H 'Authorization: Bearer ACCESS_TOKEN' \
  -H 'X-Organization-Id: ORGANIZATION_ID' \
  -H 'X-Finops-Version: 2025-06-23' \
  -H 'Content-Type: application/json' \
  -d '{
    "amount": 8500,
    "currency": "USD",
    "description": "Team lunch",
    "entity_user_id": "66d530d1-29b8-4c4f-93f5-fd43b3dc4b55",
    "merchant_amount": 8500,
    "merchant_currency": "USD",
    "merchant_location": "28 West 21st St, New York, NY 10010",
    "merchant_name": "The Smith Restaurant",
    "payment_method": {
      "type": "card",
      "details": {
        "brand": "Visa",
        "card_type": "credit",
        "last4": "4242",
        "expiry_month": 9,
        "expiry_year": 2027
      }
    },
    "started_at": "2026-05-22T12:00:00Z",
    "type": "capture"
  }'
```

The `201` response includes the new transaction's `id` and its initial `expense_status`:

```json expandable lines theme={null}
{
  "id": "792c0577-3adf-4643-9570-2b641a5b04f2",
  "amount": 8500,
  "applied_policy_id": null,
  "currency": "USD",
  "department_id": null,
  "description": "Team lunch",
  "entity_id": "c7c983b2-3c77-4682-8f3e-18c48ec0add4",
  "entity_user_id": "66d530d1-29b8-4c4f-93f5-fd43b3dc4b55",
  "expense_status": "new",
  "external_id": null,
  "has_validation_errors": false,
  "ledger_account_id": null,
  "location_id": null,
  "merchant_amount": 8500,
  "merchant_currency": "USD",
  "merchant_location": "28 West 21st St, New York, NY 10010",
  "merchant_name": "The Smith Restaurant",
  "no_receipt_reason": null,
  "partner_metadata": null,
  "payment_method": {
    "type": "card",
    "details": {
      "brand": "Visa",
      "card_type": "credit",
      "expiry_month": 9,
      "expiry_year": 2027,
      "last4": "4242"
    }
  },
  "payment_processing_status": "created",
  "receipt_id": null,
  "reject_reason": null,
  "source_of_data": {
    "department_id": null,
    "description": "user",
    "ledger_account_id": null,
    "location_id": null,
    "receipt_id": null
  },
  "started_at": "2026-05-22T12:00:00+00:00",
  "type": "capture"
}
```

`entity_user_id` ties the transaction to the employee who made the purchase — it's used by the approval engine and by access-level filtering. If you omit it, the transaction isn't associated with any user.

### Bulk-create transactions

For card feed ingestion, use `POST /transactions/bulk`. It accepts up to 5,000 transactions per request and processes them in order, so each result maps to the same index in the input. Duplicate `external_id` values within the same organization are silently skipped:

```sh expandable lines theme={null}
curl -X POST 'https://api.sandbox.tesouro.com/v1/transactions/bulk' \
  -H 'Authorization: Bearer ACCESS_TOKEN' \
  -H 'X-Organization-Id: ORGANIZATION_ID' \
  -H 'X-Finops-Version: 2025-06-23' \
  -H 'Content-Type: application/json' \
  -d '{
    "data": [
      {
        "amount": 8500,
        "currency": "USD",
        "description": "Team lunch",
        "entity_user_id": "66d530d1-29b8-4c4f-93f5-fd43b3dc4b55",
        "external_id": "card-feed-txn-0055",
        "merchant_amount": 8500,
        "merchant_currency": "USD",
        "merchant_location": "28 West 21st St, New York, NY 10010",
        "merchant_name": "The Smith Restaurant",
        "payment_method": {
          "type": "card",
          "details": {"brand": "Visa", "card_type": "credit", "last4": "4242", "expiry_month": 9, "expiry_year": 2027}
        },
        "started_at": "2026-05-22T12:00:00Z",
        "type": "capture"
      }
    ]
  }'
```

The response reports a `status` (`success`, `partial_success`, or `error`) and a `data` array where each entry has the created transaction's `id` or `null` if that record failed:

```json lines theme={null}
{
  "data": [{"id": "792c0577-3adf-4643-9570-2b641a5b04f2"}],
  "status": "success"
}
```

See [`POST /transactions/bulk`](/finops/reference/openapi/transactions/post-transactions-bulk) for the full request schema.

## Define transaction requirements

Transaction requirements control what an employee must provide before they can submit a transaction for approval. You configure requirements once per organization; they apply to every transaction in that organization.

Two fields can be made required: `receipt_id` and `description`. You can also set an `amount_threshold` so the requirement only applies to transactions above a certain value (in minor units).

### View current requirements

```sh lines theme={null}
curl -X GET 'https://api.sandbox.tesouro.com/v1/transactions/validations' \
  -H 'Authorization: Bearer ACCESS_TOKEN' \
  -H 'X-Organization-Id: ORGANIZATION_ID' \
  -H 'X-Finops-Version: 2025-06-23'
```

```json lines theme={null}
{
  "required_fields": []
}
```

### Update requirements

Replace the full requirements list with `PUT /transactions/validations`. To require a receipt on all transactions and a description on transactions over \$50:

```sh lines theme={null}
curl -X PUT 'https://api.sandbox.tesouro.com/v1/transactions/validations' \
  -H 'Authorization: Bearer ACCESS_TOKEN' \
  -H 'X-Organization-Id: ORGANIZATION_ID' \
  -H 'X-Finops-Version: 2025-06-23' \
  -H 'Content-Type: application/json' \
  -d '{
    "required_fields": [
      {"field": "receipt_id"},
      {"field": "description", "amount_threshold": 5000}
    ]
  }'
```

```json lines theme={null}
{
  "required_fields": [
    {"field": "receipt_id", "amount_threshold": null},
    {"field": "description", "amount_threshold": 5000}
  ]
}
```

`amount_threshold` is in minor units (cents). `null` means the requirement applies regardless of amount.

<Note>
  `PUT /transactions/validations` replaces the entire requirements list. To remove all requirements, send `{"required_fields": []}`.
</Note>

### Validate a transaction

Employees can use `POST /transactions/{id}/validate` to check whether a transaction satisfies the current requirements before submitting.

```sh lines theme={null}
curl -X POST 'https://api.sandbox.tesouro.com/v1/transactions/792c0577-3adf-4643-9570-2b641a5b04f2/validate' \
  -H 'Authorization: Bearer ACCESS_TOKEN' \
  -H 'X-Organization-Id: ORGANIZATION_ID' \
  -H 'X-Finops-Version: 2025-06-23'
```

If the transaction is missing a required field, the response lists what needs to be fixed:

```json lines theme={null}
{
  "id": "792c0577-3adf-4643-9570-2b641a5b04f2",
  "validation_errors": [
    {
      "field": "receipt_id",
      "message": "Field is required"
    }
  ]
}
```

A transaction that satisfies all requirements returns `"validation_errors": []`. The `has_validation_errors` field on the transaction itself reflects the same state.

See [`PUT /transactions/validations`](/finops/reference/openapi/transactions/put-transactions-validations) and [`POST /transactions/{id}/validate`](/finops/reference/openapi/transactions/post-transactions-id-validate) for the full API reference.

## Define expense approval policies

Approval policies determine which transactions require approval and who the approvers are. Tesouro evaluates policies automatically when a transaction is submitted.

Expense approval policies follow the same engine as [accounts payable approval policies](/finops/guides/accounts-payable/approvals/policies/index), with two differences:

* Set `"object_type": "transaction"` on the policy to scope it to expense transactions.
* The trigger variable is `transaction` — e.g., `{transaction.amount >= 5000}`.

<Note>
  Creating and updating approval policies requires an **organization user token** with the `approval_policy` permission. See [Authentication](/finops/guides/authentication/user-token) for how to obtain one.
</Note>

### Create an approval policy

To require approval from a specific user for any expense over \$50:

```sh expandable lines theme={null}
curl -X POST 'https://api.sandbox.tesouro.com/v1/approval-policies' \
  -H 'Authorization: Bearer USER_TOKEN' \
  -H 'X-Organization-Id: ORGANIZATION_ID' \
  -H 'X-Finops-Version: 2025-06-23' \
  -H 'Content-Type: application/json' \
  -d '{
    "name": "Expenses over $50 require approval",
    "description": "Any expense transaction over $50 must be approved by the finance manager",
    "object_type": "transaction",
    "trigger": {
      "all": [
        "{event_name == '\''submitted_for_approval'\''}",
        "{transaction.amount >= 5000}"
      ]
    },
    "script": [
      {
        "call": "ApprovalRequests.request_approval_by_users",
        "params": {
          "user_ids": ["FINANCE_MANAGER_USER_ID"],
          "required_approval_count": 1
        }
      }
    ]
  }'
```

To route approvals by role rather than by specific user ID — for example, to any user with a `finance-manager` role — use `ApprovalRequests.request_approval_by_role` in the script. See [TesouroScript reference](/finops/guides/advanced/tesouroscript) and [Script examples](/finops/guides/accounts-payable/approvals/policies/script-examples) for the full set of approval methods, including role-based and reporting-manager routing.

### Auto-approve small expenses

To approve transactions automatically when they meet a condition — for example, expenses under \$20 — use `Transactions.approve` in the script instead of an approval request:

```sh expandable lines theme={null}
curl -X POST 'https://api.sandbox.tesouro.com/v1/approval-policies' \
  -H 'Authorization: Bearer USER_TOKEN' \
  -H 'X-Organization-Id: ORGANIZATION_ID' \
  -H 'X-Finops-Version: 2025-06-23' \
  -H 'Content-Type: application/json' \
  -d '{
    "name": "Auto-approve expenses under $20",
    "description": "Small expenses are approved automatically on submission",
    "object_type": "transaction",
    "trigger": {
      "all": [
        "{event_name == '\''submitted_for_approval'\''}",
        "{transaction.amount < 2000}"
      ]
    },
    "script": [
      "{Transactions.approve(transaction.id)}"
    ]
  }'
```

When this policy matches, the transaction moves directly from `approve_in_progress` to `approved` with no human approver involved.

If multiple policies could match a transaction, the one with the highest `priority` value wins. See [Approval policy priority](/finops/guides/accounts-payable/approvals/policies/index#approval-policy-structure) for details.

See [`POST /approval-policies`](/finops/reference/openapi/approval-policies/post-approval-policies) for the full request schema.

## Configure accounting fields

Configuring accounting fields tells Tesouro which general ledger accounts are available for auto-assignment when a receipt is matched to a transaction. Without ledger accounts, Tesouro's auto-categorization step can't populate `ledger_account_id`.

### Create a ledger account

```sh lines theme={null}
curl -X POST 'https://api.sandbox.tesouro.com/v1/ledger-accounts' \
  -H 'Authorization: Bearer ACCESS_TOKEN' \
  -H 'X-Organization-Id: ORGANIZATION_ID' \
  -H 'X-Finops-Version: 2025-06-23' \
  -H 'Content-Type: application/json' \
  -d '{
    "name": "Meals & Entertainment",
    "description": "Business meals, entertainment, and client hospitality"
  }'
```

```json lines theme={null}
{
  "id": "69e70421-23fd-4c6c-bcb5-ea3794373d44",
  "created_at": "2026-05-22T12:59:10.888661+00:00",
  "updated_at": "2026-05-22T12:59:10.888676+00:00",
  "description": "Business meals, entertainment, and client hospitality",
  "is_external": false,
  "name": "Meals & Entertainment"
}
```

### List ledger accounts

```sh lines theme={null}
curl -X GET 'https://api.sandbox.tesouro.com/v1/ledger-accounts' \
  -H 'Authorization: Bearer ACCESS_TOKEN' \
  -H 'X-Organization-Id: ORGANIZATION_ID' \
  -H 'X-Finops-Version: 2025-06-23'
```

The response is a paginated list. Tesouro's auto-categorization engine fetches this same list (up to 1000 records) when selecting the best GL match for a new receipt. Keep `name` and `description` precise — the AI uses both fields to determine the right account.

See [`POST /ledger-accounts`](/finops/reference/openapi/ledger-accounts/post-ledger-accounts) and [`GET /ledger-accounts`](/finops/reference/openapi/ledger-accounts/get-ledger-accounts) for the full API reference.

## Reporting manager and expense visibility

The reporting manager relationship determines whose expenses a user can see and act on when they have the `expense:read:self` or `expense:write:self` permission. "Self" includes the user's own transactions **plus** those of any users who list them as their reporting manager.

This matters for two common setups:

* A **Finance admin** assigned `expense:read:self` sees their own expenses and any direct reports' expenses automatically.
* An **Employee** similarly sees only their own expenses by default, but sees their direct reports' expenses if any users list them as reporting manager.

### Designate a reporting manager

Reporting manager is a field on the user model — not a role or permission. Set it when creating or updating a user by including `reportingManagerId` in the request body:

```sh lines theme={null}
curl -X PATCH 'https://api.sandbox.tesouro.com/identity/v1/users/USER_ID' \
  -H 'Authorization: Bearer ACCESS_TOKEN' \
  -H 'X-Organization-Id: ORGANIZATION_ID' \
  -H 'X-Finops-Version: 2025-06-23' \
  -H 'Content-Type: application/json' \
  -d '{
    "reportingManagerUserId": "USER_ID"
  }'
```

Any user can be a reporting manager — there is no special role or toggle required.

### Expense visibility and approvals

Having reporting manager access does **not** automatically grant approval rights. Whether a reporting manager can approve their team's expenses is determined by the organization's approval policies. See [Approval policies](/finops/guides/accounts-payable/approvals/policies/index) for how to route approvals by reporting manager.
