Skip to main content

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.

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:
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:
{
  "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:
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:
{
  "data": [{"id": "792c0577-3adf-4643-9570-2b641a5b04f2"}],
  "status": "success"
}
See 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

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'
{
  "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:
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}
    ]
  }'
{
  "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.
PUT /transactions/validations replaces the entire requirements list. To remove all requirements, send {"required_fields": []}.

Validate a transaction

Employees can use POST /transactions/{id}/validate to check whether a transaction satisfies the current requirements before submitting.
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:
{
  "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 and 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, 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}.
Creating and updating approval policies requires an organization user token with the approval_policy permission. See Authentication for how to obtain one.

Create an approval policy

To require approval from a specific user for any expense over $50:
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 and 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:
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 for details. See 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

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"
  }'
{
  "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

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 and GET /ledger-accounts for the full API reference.