ASAP Expenses API

Purchases: what the org paid, with VAT per rate. A draft counts for nothing until confirmed.

Operations 11

GET /v1/expenses GET /v1/expenses #
POST /v1/expenses POST /v1/expenses #
POST /v1/expenses/from-inbound/{inbound_id} POST /v1/expenses/from-inbound/{inbound_id} #
POST /v1/expenses/receipts POST /v1/expenses/receipts #
GET /v1/expenses/register GET /v1/expenses/register #
GET /v1/expenses/register.csv GET /v1/expenses/register.csv #
GET /v1/expenses/{id} GET /v1/expenses/{id} #
DELETE /v1/expenses/{id} DELETE /v1/expenses/{id} #
PATCH /v1/expenses/{id} PATCH /v1/expenses/{id} #
POST /v1/expenses/{id}/confirm POST /v1/expenses/{id}/confirm #
GET /v1/expenses/{id}/receipt GET /v1/expenses/{id}/receipt #

Work with this as data

Every API here is available over the APIs.io API and to AI agents over MCP.

MCP server

One button, every client — Claude, Cursor, VS Code and the rest.

https://apis.io/mcp

Tools for apis

7 MCP tools reach this
  • find_apisBrowse and filter every API in the catalog.
  • get_api_artifactsOne API's artifacts, grouped by type.
  • get_openapiThe primary OpenAPI for this API.
  • find_similar_apisAPIs that look like this one.
  • apis_io_searchSTART HERE — APIs, providers and tags for one query, each with its total.
  • resolveTurn a domain, URL or GitHub org into the provider it belongs to.
  • find_cohortsEvery scored population of providers in the catalog.
All 92 tools →

Call it yourself

curl for this page
This API
curl "https://apis.io/api/v1/apis/asap-cool:asap-cool-expenses-api"
All apis
curl "https://apis.io/api/v1/apis?limit=25"

Discovery needs no key. Ratings and market analysis are Pro.

Get an API key

Free tier, no form to fill in. Signing in shares your email address with us — we store it to create your key and to recognise you if you sign in with another provider. See our Privacy Policy and Terms.

A second provider on the same verified email joins the account you already have.

OpenAPI Specification

asap-cool-expenses-api-openapi.yml Raw ↑
openapi: 3.2.0
info:
  title: ASAP Platform Expenses API
  description: 'Public HTTP API for ASAP Platform (api.asap.cool).


    All `/v1` responses are wrapped in a standard envelope. Success responses are `{ "data": , "meta": { "request_id", "next_cursor"?, "has_more"? } }` — the schema documented for each operation describes the inner `data` value. Errors are `{ "error": { "code", "message", "field"?, "request_id" } }`. Every response also carries an `X-Request-Id` header matching `meta.request_id` / `error.request_id`.'
  license:
    name: BUSL-1.1
  version: 0.0.0
servers:
- url: https://api.asap.cool
  description: Production
tags:
- name: Expenses
  description: 'Purchases: what the org paid, with VAT per rate. A draft counts for nothing until confirmed.'
paths:
  /v1/expenses:
    get:
      tags:
      - Expenses
      summary: GET /v1/expenses
      operationId: listExpenses
      parameters:
      - name: status
        in: query
        description: Only this status.
        required: false
        schema:
          oneOf:
          - type: 'null'
          - $ref: '#/components/schemas/StatusView'
      - name: from
        in: query
        description: Incurred on or after this day, `YYYY-MM-DD`.
        required: false
        schema:
          type:
          - string
          - 'null'
      - name: to
        in: query
        description: Incurred on or before this day, `YYYY-MM-DD`.
        required: false
        schema:
          type:
          - string
          - 'null'
      - name: category
        in: query
        description: Only this category.
        required: false
        schema:
          oneOf:
          - type: 'null'
          - $ref: '#/components/schemas/CategoryView'
      responses:
        '200':
          description: Expenses matching the filters, most recent first.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ExpenseListView'
        '401':
          description: No active org.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorBody'
        '403':
          description: Missing invoice:read scope.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorBody'
        '422':
          description: A date filter is not YYYY-MM-DD.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorBody'
      security:
      - api_key:
        - invoice:read
      - session: []
    post:
      tags:
      - Expenses
      summary: POST /v1/expenses
      operationId: createExpense
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateExpenseRequest'
        required: true
      responses:
        '201':
          description: Draft stored; it counts for nothing until confirmed.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ExpenseView'
        '401':
          description: No active org.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorBody'
        '403':
          description: Missing invoice:write scope.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorBody'
        '422':
          description: A field is malformed, or `file_not_found` (not a file of this org).
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorBody'
      security:
      - api_key:
        - invoice:write
      - session: []
  /v1/expenses/from-inbound/{inbound_id}:
    post:
      tags:
      - Expenses
      summary: POST /v1/expenses/from-inbound/{inbound_id}
      operationId: createExpenseFromInbound
      parameters:
      - name: inbound_id
        in: path
        description: Received e-invoice identifier.
        required: true
        schema:
          type: string
          format: uuid
      responses:
        '201':
          description: Draft prefilled from the e-invoice's parsed figures, with one VAT line whose rate is inferred. No AI is involved and no credit is used. It stays a draft for the user to check.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ExpenseView'
        '401':
          description: No active org.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorBody'
        '403':
          description: Missing invoice:write scope.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorBody'
        '404':
          description: Received invoice not found, or cross-org.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorBody'
        '409':
          description: '`expense_exists`: an expense already points at this e-invoice.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorBody'
        '422':
          description: '`inbound_not_parsed`: the e-invoice''s figures could not be read.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorBody'
      security:
      - api_key:
        - invoice:write
      - session: []
  /v1/expenses/receipts:
    post:
      tags:
      - Expenses
      summary: POST /v1/expenses/receipts
      operationId: uploadExpenseReceipt
      requestBody:
        description: 'multipart/form-data with a `file` part: a PDF, JPEG or PNG receipt.'
        content:
          multipart/form-data:
            schema:
              type: string
        required: true
      responses:
        '200':
          description: This receipt is already an expense; it is returned as is and no credit is used.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ExpenseView'
        '201':
          description: 'Receipt stored and read. A PDF that embeds its Factur-X data is read from it (`extraction_status` `none`, no AI credit, works with AI off). Otherwise the draft carries what the model read (`read`), or nothing (`failed`) when the read did not work: fill it by hand with the receipt beside the form. One AI credit is used either way.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ExpenseView'
        '401':
          description: No active org.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorBody'
        '402':
          description: Monthly AI credits exhausted (`plan_limit_reached`); nothing was stored. `POST /v1/expenses` takes the figures by hand and needs no credit.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorBody'
        '403':
          description: Missing invoice:write scope.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorBody'
        '413':
          description: Body exceeds the upload limit.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorBody'
        '422':
          description: '`upload_rejected` (empty, too large, not accepted) or `receipt_type_unsupported` (not a PDF, JPEG or PNG).'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorBody'
        '429':
          description: Storage quota exhausted (`storage_quota_exceeded`).
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorBody'
        '503':
          description: Receipt reading is not configured (`ai_unavailable`); nothing was stored.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorBody'
      security:
      - api_key:
        - invoice:write
      - session: []
  /v1/expenses/register:
    get:
      tags:
      - Expenses
      summary: GET /v1/expenses/register
      operationId: getExpenseRegister
      parameters:
      - name: from
        in: query
        description: First day of the period, `YYYY-MM-DD` (required).
        required: false
        schema:
          type:
          - string
          - 'null'
      - name: to
        in: query
        description: Last day of the period, `YYYY-MM-DD`, inclusive (required).
        required: false
        schema:
          type:
          - string
          - 'null'
      responses:
        '200':
          description: Totals of the confirmed expenses of the period; deductible VAT is null in franchise en base.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/RegisterView'
        '401':
          description: No active org.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorBody'
        '403':
          description: Missing invoice:read scope.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorBody'
        '422':
          description: '`from` or `to` missing or not YYYY-MM-DD, `to` before `from`, or `register_too_large`.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorBody'
      security:
      - api_key:
        - invoice:read
      - session: []
  /v1/expenses/register.csv:
    get:
      tags:
      - Expenses
      summary: GET /v1/expenses/register.csv
      operationId: exportExpenseRegister
      parameters:
      - name: from
        in: query
        description: First day of the period, `YYYY-MM-DD` (required).
        required: false
        schema:
          type:
          - string
          - 'null'
      - name: to
        in: query
        description: Last day of the period, `YYYY-MM-DD`, inclusive (required).
        required: false
        schema:
          type:
          - string
          - 'null'
      responses:
        '200':
          description: 'CSV attachment: one row per VAT line of a confirmed expense.'
          content:
            text/csv:
              schema:
                type: string
        '401':
          description: No active org.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorBody'
        '403':
          description: Missing invoice:read scope.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorBody'
        '422':
          description: '`from` or `to` missing or not YYYY-MM-DD, `to` before `from`, or `register_too_large`.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorBody'
      security:
      - api_key:
        - invoice:read
      - session: []
  /v1/expenses/{id}:
    get:
      tags:
      - Expenses
      summary: GET /v1/expenses/{id}
      operationId: getExpense
      parameters:
      - name: id
        in: path
        description: Expense identifier.
        required: true
        schema:
          type: string
          format: uuid
      responses:
        '200':
          description: The expense.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ExpenseView'
        '401':
          description: No active org.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorBody'
        '403':
          description: Missing invoice:read scope.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorBody'
        '404':
          description: Not found, or cross-org.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorBody'
      security:
      - api_key:
        - invoice:read
      - session: []
    delete:
      tags:
      - Expenses
      summary: DELETE /v1/expenses/{id}
      operationId: deleteExpense
      parameters:
      - name: id
        in: path
        description: Expense identifier.
        required: true
        schema:
          type: string
          format: uuid
      responses:
        '204':
          description: Deleted. The receipt stays in the file library.
        '401':
          description: No active org.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorBody'
        '403':
          description: Missing invoice:write scope.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorBody'
        '404':
          description: Not found, or cross-org.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorBody'
      security:
      - api_key:
        - invoice:write
      - session: []
    patch:
      tags:
      - Expenses
      summary: PATCH /v1/expenses/{id}
      operationId: updateExpense
      parameters:
      - name: id
        in: path
        description: Expense identifier.
        required: true
        schema:
          type: string
          format: uuid
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/UpdateExpenseRequest'
        required: true
      responses:
        '200':
          description: The updated expense. A confirmed expense stays confirmed, so the edit must still pass the confirmation checks.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ExpenseView'
        '401':
          description: No active org.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorBody'
        '403':
          description: Missing invoice:write scope.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorBody'
        '404':
          description: Not found, or cross-org.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorBody'
        '422':
          description: A field is malformed or, on a confirmed expense, the result is inconsistent.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorBody'
      security:
      - api_key:
        - invoice:write
      - session: []
  /v1/expenses/{id}/confirm:
    post:
      tags:
      - Expenses
      summary: POST /v1/expenses/{id}/confirm
      operationId: confirmExpense
      parameters:
      - name: id
        in: path
        description: Expense identifier.
        required: true
        schema:
          type: string
          format: uuid
      responses:
        '200':
          description: The expense is confirmed (also when it already was).
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ExpenseView'
        '401':
          description: No active org.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorBody'
        '403':
          description: Missing invoice:write scope.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorBody'
        '404':
          description: Not found, or cross-org.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorBody'
        '422':
          description: The expense is incomplete or inconsistent; the error names the field.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorBody'
      security:
      - api_key:
        - invoice:write
      - session: []
  /v1/expenses/{id}/receipt:
    get:
      tags:
      - Expenses
      summary: GET /v1/expenses/{id}/receipt
      operationId: downloadExpenseReceipt
      parameters:
      - name: id
        in: path
        description: Expense identifier.
        required: true
        schema:
          type: string
          format: uuid
      responses:
        '307':
          description: 'Redirect to the stored receipt: the library file (`/v1/files/{id}/content`) or, for an expense recorded from a received e-invoice, its PDF (`/v1/invoices/inbound/{id}/pdf`). Those routes check their own scope and the org.'
        '401':
          description: No active org.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorBody'
        '403':
          description: Missing invoice:read scope.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorBody'
        '404':
          description: Expense not found, cross-org, or it has no receipt (`receipt_not_found`).
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorBody'
      security:
      - api_key:
        - invoice:read
      - session: []
components:
  schemas:
    VatLineView:
      type: object
      description: One VAT rate of an expense.
      required:
      - rate
      - base_cents
      - vat_cents
      - deductible_cents
      properties:
        base_cents:
          type: integer
          format: int64
          description: Amount before VAT, in cents.
        deductible_cents:
          type: integer
          format: int64
          description: Recoverable part of the VAT, in cents.
        rate:
          type: string
          description: Rate in percent (`"20"`, `"5.5"`).
        vat_cents:
          type: integer
          format: int64
          description: VAT amount, in cents.
    CreateExpenseRequest:
      type: object
      description: Body of `POST /v1/expenses`.
      properties:
        category:
          oneOf:
          - type: 'null'
          - $ref: '#/components/schemas/CategoryView'
            description: Advisory category; defaults to `other`.
        currency:
          type:
          - string
          - 'null'
          description: ISO currency code; defaults to `eur`.
        file_id:
          type:
          - string
          - 'null'
          format: uuid
          description: A file of the library to attach as the receipt.
        incurred_on:
          type:
          - string
          - 'null'
          description: Day the expense was incurred, `YYYY-MM-DD`.
        reference:
          type:
          - string
          - 'null'
          description: The supplier's invoice or ticket number.
        supplier_name:
          type:
          - string
          - 'null'
          description: Who was paid.
        total_ttc:
          type:
          - integer
          - 'null'
          format: int64
          description: Total paid, in cents.
        vat_lines:
          type: array
          items:
            $ref: '#/components/schemas/VatLineInput'
          description: VAT per rate (10 lines at most).
    ExpenseView:
      type: object
      description: An expense.
      required:
      - id
      - status
      - category
      - currency
      - total_ht
      - total_vat
      - vat_deductible
      - vat_lines
      - extraction_status
      - created_at
      - updated_at
      properties:
        category:
          $ref: '#/components/schemas/CategoryView'
          description: Advisory category.
        confirmed_at:
          type:
          - string
          - 'null'
          description: RFC 3339 UTC, set once confirmed.
        created_at:
          type: string
          description: RFC 3339 UTC.
        currency:
          type: string
          description: Lower-case ISO currency code; only `eur` can be confirmed.
        extraction_status:
          type: string
          description: '`none`, `read` (prefilled by AI) or `failed` (the read failed; fill it by hand).'
        file_id:
          type:
          - string
          - 'null'
          format: uuid
          description: Receipt in the file library.
        id:
          type: string
          format: uuid
          description: Expense identifier.
        inbound_invoice_id:
          type:
          - string
          - 'null'
          format: uuid
          description: Received e-invoice this expense was recorded from.
        incurred_on:
          type:
          - string
          - 'null'
          description: Day the expense was incurred, `YYYY-MM-DD`.
        reference:
          type:
          - string
          - 'null'
          description: The supplier's invoice or ticket number.
        status:
          $ref: '#/components/schemas/StatusView'
          description: '`draft` or `confirmed`.'
        supplier_name:
          type:
          - string
          - 'null'
          description: Who was paid.
        total_ht:
          type: integer
          format: int64
          description: Sum of the lines' bases, in cents.
        total_ttc:
          type:
          - integer
          - 'null'
          format: int64
          description: Total paid, in cents.
        total_vat:
          type: integer
          format: int64
          description: Sum of the lines' VAT, in cents.
        updated_at:
          type: string
          description: RFC 3339 UTC.
        vat_deductible:
          type: integer
          format: int64
          description: Sum of the lines' recoverable VAT, in cents.
        vat_lines:
          type: array
          items:
            $ref: '#/components/schemas/VatLineView'
          description: VAT per rate.
    StatusView:
      type: string
      description: Expense lifecycle.
      enum:
      - draft
      - confirmed
    RegisterCategoryView:
      type: object
      description: Sums for one category.
      required:
      - category
      - amounts
      properties:
        amounts:
          $ref: '#/components/schemas/RegisterAmounts'
          description: Its sums.
        category:
          $ref: '#/components/schemas/CategoryView'
          description: The category.
    RegisterAmounts:
      type: object
      description: Sums, in cents.
      required:
      - ht
      - vat
      - ttc
      properties:
        ht:
          type: integer
          format: int64
          description: Before VAT.
        ttc:
          type: integer
          format: int64
          description: Total paid.
        vat:
          type: integer
          format: int64
          description: VAT paid.
        vat_deductible:
          type:
          - integer
          - 'null'
          format: int64
          description: Recoverable VAT; `null` when the org is in franchise en base.
    RegisterRateView:
      type: object
      description: Sums for one VAT rate.
      required:
      - rate
      - amounts
      properties:
        amounts:
          $ref: '#/components/schemas/RegisterAmounts'
          description: Its sums.
        rate:
          type: string
          description: Rate in percent (`"20"`, `"5.5"`).
    RegisterView:
      type: object
      description: The purchases register of a period.
      required:
      - from
      - to
      - expense_count
      - total
      - by_category
      - by_rate
      properties:
        by_category:
          type: array
          items:
            $ref: '#/components/schemas/RegisterCategoryView'
          description: Per category, categories without expenses left out.
        by_rate:
          type: array
          items:
            $ref: '#/components/schemas/RegisterRateView'
          description: Per VAT rate, highest first.
        expense_count:
          type: integer
          format: int64
          description: Confirmed expenses in the period.
        from:
          type: string
          description: First day, `YYYY-MM-DD`.
        to:
          type: string
          description: Last day, `YYYY-MM-DD`.
        total:
          $ref: '#/components/schemas/RegisterAmounts'
          description: Everything.
        vat_regime:
          type:
          - string
          - 'null'
          description: The org's VAT regime (`subject`, `franchise_base`, `real`) when known.
    ErrorBody:
      type: object
      description: 'Generic error body returned by API handlers when they fail. `code` is a

        stable machine-readable token; `message` is human-friendly and meant for

        surfacing to end users.'
      required:
      - code
      - message
      properties:
        code:
          type: string
          description: Stable machine token (e.g. `invalid_credentials`).
        field:
          type:
          - string
          - 'null'
          description: Offending request field for validation errors, when known.
        message:
          type: string
          description: Human-readable message.
    ExpenseListView:
      type: object
      description: Response of `GET /v1/expenses`.
      required:
      - expenses
      properties:
        expenses:
          type: array
          items:
            $ref: '#/components/schemas/ExpenseView'
          description: Matching expenses, most recent first (200 at most).
    CategoryView:
      type: string
      description: Advisory expense category.
      enum:
      - software
      - hardware
      - telecom
      - travel
      - meals
      - fees
      - insurance
      - marketing
      - rent
      - training
      - other
    VatLineInput:
      type: object
      description: One VAT rate of an expense, as sent by the client.
      required:
      - rate
      - base_cents
      - vat_cents
      properties:
        base_cents:
          type: integer
          format: int64
          description: Amount before VAT, in cents.
        deductible_cents:
          type:
          - integer
          - 'null'
          format: int64
          description: Recoverable part of the VAT, in cents; defaults to the whole VAT.
        rate:
          type: string
          description: Rate in percent as a decimal token (`"20"`, `"5.5"`).
        vat_cents:
          type: integer
          format: int64
          description: VAT amount, in cents.
    UpdateExpenseRequest:
      type: object
      description: 'Body of `PATCH /v1/expenses/{id}`. A missing field is left as it is; a blank

        text clears it; `vat_lines`, when present, replaces all the lines.'
      properties:
        category:
          oneOf:
          - type: 'null'
          - $ref: '#/components/schemas/CategoryView'
            description: Advisory category.
        currency:
          type:
          - string
          - 'null'
          description: ISO currency code.
        incurred_on:
          type:
          - string
          - 'null'
          description: Day the expense was incurred, `YYYY-MM-DD`.
        reference:
          type:
          - string
          - 'null'
          description: The supplier's invoice or ticket number.
        supplier_name:
          type:
          - string
          - 'null'
          description: Who was paid.
        total_ttc:
          type:
          - integer
          - 'null'
          format: int64
          description: Total paid, in cents.
        vat_lines:
          type:
          - array
          - 'null'
          items:
            $ref: '#/components/schemas/VatLineInput'
          description: New VAT lines, replacing the current ones.
  securitySchemes:
    api_key:
      type: http
      scheme: bearer
    session:
      type: apiKey
      in: cookie
      name: asap_session