Expenses

Expense logging, OCR scanning, email ingestion, vendor matching, and approval workflow.

21 endpoints·Scopes: expenses:read, expenses:write
GET/api/expense-categories#

List expense categories

scope · expenses:read

Lists expense categories the caller can see. Results are filtered by the caller's accessible organizations; many routes paginate via page and limit.

Scope: expenses:read
Endpoint: GET /api/expense-categories

Example request

curl "https://guliel.com/api/expense-categories?organizationId=value" \
  -H "Authorization: Bearer $GULIEL_API_KEY"

Query parameters

  • organizationIdreq
    string

Response (200/201)

  • categoriesreq
    array<object>
    • idreq
      string
    • organizationIdreq
      string
    • namereq
      string
    • colorreq
      string | null
    • isDefaultreq
      boolean
    • _count
      object
      • expensesreq
        integer
    • createdAtreq
      string
    • updatedAtreq
      string

Error responses

400Validation
401Unauthenticated
403Forbidden
500Internal
POST/api/expense-categories#

Create an expense category

scope · expenses:writerole · CONTRIBUTOR+

Creates a new expense category in the target organization. Returns the created record on success.

Scope: expenses:write
Min role: CONTRIBUTOR or higher
Endpoint: POST /api/expense-categories

Example request

curl -X POST "https://guliel.com/api/expense-categories" \
  -H "Authorization: Bearer $GULIEL_API_KEY" \
  -H "Content-Type: application/json" \
  --data '{
  "organizationId": "abc123",
  "name": "Sample"
}'

Request body

  • organizationIdreq
    string
  • namereq
    string
  • color
    string

Response (200/201)

  • idreq
    string
  • organizationIdreq
    string
  • namereq
    string
  • colorreq
    string | null
  • isDefaultreq
    boolean
  • _count
    object
    • expensesreq
      integer
  • createdAtreq
    string
  • updatedAtreq
    string

Error responses

400Validation
401Unauthenticated
403Forbidden
429RateLimit
500Internal
DELETE/api/expense-categories/{id}#

Delete an expense category

scope · expenses:write

Deletes the expense category. Some endpoints hard-delete the row, others soft-revoke (set revokedAt). Check the response shape.

Scope: expenses:write
Endpoint: DELETE /api/expense-categories/{id}

Example request

curl -X DELETE "https://guliel.com/api/expense-categories/abc123?id=value" \
  -H "Authorization: Bearer $GULIEL_API_KEY"

Query parameters

  • idreq
    string

Response (200/201)

  • successreq
    boolean

Error responses

400Validation
401Unauthenticated
403Forbidden
429RateLimit
500Internal
PATCH/api/expense-categories/{id}#

Update an expense category

scope · expenses:write

Updates an existing expense category. Only the fields present in the request body are changed; omitted fields stay as-is.

Scope: expenses:write
Endpoint: PATCH /api/expense-categories/{id}

Example request

curl -X PATCH "https://guliel.com/api/expense-categories/abc123" \
  -H "Authorization: Bearer $GULIEL_API_KEY" \
  -H "Content-Type: application/json" \
  --data '{
  "id": "abc123"
}'

Request body

  • idreq
    string
  • name
    string
  • color
    string | null

Response (200/201)

  • idreq
    string
  • organizationIdreq
    string
  • namereq
    string
  • colorreq
    string | null
  • isDefaultreq
    boolean
  • createdAtreq
    string
  • updatedAtreq
    string

Error responses

400Validation
401Unauthenticated
403Forbidden
429RateLimit
500Internal
PUT/api/expense-categories/{id}#

Update an expense category

scope · expenses:write

Replaces an existing expense category. All required fields must be supplied — omitted fields revert to their schema defaults.

Scope: expenses:write
Endpoint: PUT /api/expense-categories/{id}

Example request

curl -X PUT "https://guliel.com/api/expense-categories/abc123" \
  -H "Authorization: Bearer $GULIEL_API_KEY" \
  -H "Content-Type: application/json" \
  --data '{
  "id": "abc123"
}'

Request body

  • idreq
    string
  • name
    string
  • color
    string | null

Response (200/201)

  • idreq
    string
  • organizationIdreq
    string
  • namereq
    string
  • colorreq
    string | null
  • isDefaultreq
    boolean
  • createdAtreq
    string
  • updatedAtreq
    string

Error responses

400Validation
401Unauthenticated
403Forbidden
429RateLimit
500Internal
GET/api/expenses#

List expenses

scope · expenses:read

Lists expenses the caller can see. Results are filtered by the caller's accessible organizations; many routes paginate via page and limit.

Scope: expenses:read
Endpoint: GET /api/expenses

Example request

curl "https://guliel.com/api/expenses?organizationId=value" \
  -H "Authorization: Bearer $GULIEL_API_KEY"

Query parameters

  • organizationIdreq
    string
  • pagereq
    integer
  • limitreq
    integer
  • searchreq
    string
  • categoryId
    string
  • status
    string
  • source
    string
  • dateFrom
    string
  • dateTo
    string
  • minAmount
    string
  • maxAmount
    string
  • customerId
    string
  • sortByreq
    enum<4>
    date · amount · vendor · createdAt
  • sortOrderreq
    enum<2>
    asc · desc

Response (200/201)

  • expensesreq
    array<object>
    • idreq
      string
    • organizationIdreq
      string
    • descriptionreq
      string | null
    • amountreq
      number
    • currencyreq
      string
    • datereq
      string
    • categoryreq
      object | null
    • vendorreq
      string | null
    • paymentMethodreq
      string | null
    • sourcereq
      string
    • statusreq
      string
    • attachments
      array<object>
    • customers
      array<object>
      • customerIdreq
        string
      • customerreq
        object | null
    • createdByreq
      object | null
    • createdAtreq
      string
    • updatedAtreq
      string
  • paginationreq
    object
    • pagereq
      integer
    • limitreq
      integer
    • totalreq
      integer
    • totalPagesreq
      integer

Error responses

400Validation
401Unauthenticated
403Forbidden
500Internal
POST/api/expenses#

Create an expense

scope · expenses:writerole · CONTRIBUTOR+

Creates a new expense in the target organization. Returns the created record on success.

Scope: expenses:write
Min role: CONTRIBUTOR or higher
Endpoint: POST /api/expenses

Example request

curl -X POST "https://guliel.com/api/expenses" \
  -H "Authorization: Bearer $GULIEL_API_KEY" \
  -H "Content-Type: application/json" \
  --data '{
  "organizationId": "abc123",
  "amount": 100,
  "currency": "USD",
  "date": "string",
  "source": "MANUAL"
}'

Request body

  • organizationIdreq
    string
  • title
    string
  • vendor
    string
  • amountreq
    number
  • currencyreq
    string
  • datereq
    string
  • categoryId
    string
  • notes
    string
  • taxAmount
    number
  • taxRate
    number
  • paymentMethod
    string
  • sourcereq
    enum<3>
    MANUAL · SCAN · EMAIL
  • customerIds
    array<string>

Response (200/201)

  • idreq
    string
  • organizationIdreq
    string
  • descriptionreq
    string | null
  • amountreq
    number
  • currencyreq
    string
  • datereq
    string
  • categoryreq
    object | null
  • vendorreq
    string | null
  • paymentMethodreq
    string | null
  • sourcereq
    string
  • statusreq
    string
  • attachments
    array<object>
  • customers
    array<object>
    • customerIdreq
      string
    • customerreq
      object | null
  • createdByreq
    object | null
  • createdAtreq
    string
  • updatedAtreq
    string

Error responses

400Validation
401Unauthenticated
403Forbidden
429RateLimit
500Internal
DELETE/api/expenses/{id}#

Delete an expense

scope · expenses:write

Deletes the expense. Some endpoints hard-delete the row, others soft-revoke (set revokedAt). Check the response shape.

Scope: expenses:write
Endpoint: DELETE /api/expenses/{id}

Example request

curl -X DELETE "https://guliel.com/api/expenses/abc123?id=value" \
  -H "Authorization: Bearer $GULIEL_API_KEY"

Query parameters

  • idreq
    string

Response (200/201)

  • successreq
    boolean

Error responses

400Validation
401Unauthenticated
403Forbidden
429RateLimit
500Internal
GET/api/expenses/{id}#

Get an expense

scope · expenses:read

Fetches a single expense by id. Returns 404 if the expense isn't in the caller's accessible organizations.

Scope: expenses:read
Endpoint: GET /api/expenses/{id}

Example request

curl "https://guliel.com/api/expenses/abc123?id=value" \
  -H "Authorization: Bearer $GULIEL_API_KEY"

Query parameters

  • idreq
    string

Response (200/201)

  • idreq
    string
  • organizationIdreq
    string
  • titlereq
    string | null
  • descriptionreq
    string | null
  • vendorreq
    string | null
  • amountreq
    number
  • currencyreq
    string
  • datereq
    string
  • statusreq
    string
  • paymentMethodreq
    string | null
  • taxAmountreq
    number | null
  • taxRatereq
    number | null
  • notesreq
    string | null
  • sourcereq
    string
  • emailMessageIdreq
    string | null
  • categoryreq
    object | null
  • customersreq
    array<object>
    • customerIdreq
      string
    • customerreq
      object | null
  • attachmentsreq
    array<object>
  • createdByreq
    object | null
  • order
    object | null
  • createdAtreq
    string
  • updatedAtreq
    string

Error responses

400Validation
401Unauthenticated
403Forbidden
500Internal
PATCH/api/expenses/{id}#

Update an expense

scope · expenses:write

Updates an existing expense. Only the fields present in the request body are changed; omitted fields stay as-is.

Scope: expenses:write
Endpoint: PATCH /api/expenses/{id}

Example request

curl -X PATCH "https://guliel.com/api/expenses/abc123" \
  -H "Authorization: Bearer $GULIEL_API_KEY" \
  -H "Content-Type: application/json" \
  --data '{
  "id": "abc123"
}'

Request body

  • idreq
    string
  • title
    string | null
  • vendor
    string | null
  • amount
    number
  • currency
    string
  • date
    string
  • categoryId
    string | null
  • notes
    string | null
  • taxAmount
    number | null
  • taxRate
    number | null
  • paymentMethod
    string | null

Response (200/201)

  • idreq
    string
  • organizationIdreq
    string
  • titlereq
    string | null
  • descriptionreq
    string | null
  • vendorreq
    string | null
  • amountreq
    number
  • currencyreq
    string
  • datereq
    string
  • statusreq
    string
  • paymentMethodreq
    string | null
  • taxAmountreq
    number | null
  • taxRatereq
    number | null
  • notesreq
    string | null
  • sourcereq
    string
  • emailMessageIdreq
    string | null
  • categoryreq
    object | null
  • customersreq
    array<object>
    • customerIdreq
      string
    • customerreq
      object | null
  • attachmentsreq
    array<object>
  • createdByreq
    object | null
  • order
    object | null
  • createdAtreq
    string
  • updatedAtreq
    string

Error responses

400Validation
401Unauthenticated
403Forbidden
429RateLimit
500Internal
PUT/api/expenses/{id}#

Update an expense

scope · expenses:write

Replaces an existing expense. All required fields must be supplied — omitted fields revert to their schema defaults.

Scope: expenses:write
Endpoint: PUT /api/expenses/{id}

Example request

curl -X PUT "https://guliel.com/api/expenses/abc123" \
  -H "Authorization: Bearer $GULIEL_API_KEY" \
  -H "Content-Type: application/json" \
  --data '{
  "id": "abc123"
}'

Request body

  • idreq
    string
  • title
    string | null
  • vendor
    string | null
  • amount
    number
  • currency
    string
  • date
    string
  • categoryId
    string | null
  • notes
    string | null
  • taxAmount
    number | null
  • taxRate
    number | null
  • paymentMethod
    string | null

Response (200/201)

  • idreq
    string
  • organizationIdreq
    string
  • titlereq
    string | null
  • descriptionreq
    string | null
  • vendorreq
    string | null
  • amountreq
    number
  • currencyreq
    string
  • datereq
    string
  • statusreq
    string
  • paymentMethodreq
    string | null
  • taxAmountreq
    number | null
  • taxRatereq
    number | null
  • notesreq
    string | null
  • sourcereq
    string
  • emailMessageIdreq
    string | null
  • categoryreq
    object | null
  • customersreq
    array<object>
    • customerIdreq
      string
    • customerreq
      object | null
  • attachmentsreq
    array<object>
  • createdByreq
    object | null
  • order
    object | null
  • createdAtreq
    string
  • updatedAtreq
    string

Error responses

400Validation
401Unauthenticated
403Forbidden
429RateLimit
500Internal
DELETE/api/expenses/{id}/customers#

Detach customers from an expense

scope · expenses:write

Removes the association without deleting the underlying record.

Scope: expenses:write
Endpoint: DELETE /api/expenses/{id}/customers

Example request

curl -X DELETE "https://guliel.com/api/expenses/abc123/customers?id=value" \
  -H "Authorization: Bearer $GULIEL_API_KEY"

Query parameters

  • idreq
    string
  • customerIdreq
    string

Response (200/201)

  • successreq
    boolean

Error responses

400Validation
401Unauthenticated
403Forbidden
429RateLimit
500Internal
POST/api/expenses/{id}/customers#

Attach customers to an expense

scope · expenses:write

Creates a new customers attached to the given expense.

Scope: expenses:write
Endpoint: POST /api/expenses/{id}/customers

Example request

curl -X POST "https://guliel.com/api/expenses/abc123/customers" \
  -H "Authorization: Bearer $GULIEL_API_KEY" \
  -H "Content-Type: application/json" \
  --data '{
  "id": "abc123",
  "customerIds": [
    "string"
  ]
}'

Request body

  • idreq
    string
  • customerIdsreq
    array<string>

Response (200/201)

JSON object. The exact response shape is being progressively documented — call the endpoint with a real access token to see the live structure, or check the type definitions exposed by the official client libraries.

Error responses

400Validation
401Unauthenticated
403Forbidden
429RateLimit
500Internal
PATCH/api/expenses/{id}/status#

Update the expense status

scope · expenses:write

Mutates the state machine of the expense. Some transitions are restricted by role.

Scope: expenses:write
Endpoint: PATCH /api/expenses/{id}/status

Example request

curl -X PATCH "https://guliel.com/api/expenses/abc123/status" \
  -H "Authorization: Bearer $GULIEL_API_KEY" \
  -H "Content-Type: application/json" \
  --data '{
  "id": "abc123",
  "status": "DRAFT"
}'

Request body

  • idreq
    string
  • statusreq
    enum<4>
    DRAFT · PENDING_REVIEW · APPROVED · REJECTED
  • reason
    string

Response (200/201)

JSON object. The exact response shape is being progressively documented — call the endpoint with a real access token to see the live structure, or check the type definitions exposed by the official client libraries.

Error responses

400Validation
401Unauthenticated
403Forbidden
429RateLimit
500Internal
GET/api/expenses/attachments/{id}#

Attachment streaming for expenses

scope · expenses:read

Serves the raw attached file for an expense. The URL embeds a signed token; clients shouldn't construct it directly.

Scope: expenses:read
Endpoint: GET /api/expenses/attachments/{id}

Example request

curl "https://guliel.com/api/expenses/attachments/abc123?id=value" \
  -H "Authorization: Bearer $GULIEL_API_KEY"

Query parameters

  • idreq
    string

Response (200/201)

JSON object. The exact response shape is being progressively documented — call the endpoint with a real access token to see the live structure, or check the type definitions exposed by the official client libraries.

Error responses

400Validation
401Unauthenticated
403Forbidden
500Internal
POST/api/expenses/email-poll#

Poll the expense inbox

scope · expenses:write

Pulls newly-arrived receipts from the connected mailbox and creates expenses for each one.

Scope: expenses:write
Endpoint: POST /api/expenses/email-poll

Example request

curl -X POST "https://guliel.com/api/expenses/email-poll" \
  -H "Authorization: Bearer $GULIEL_API_KEY" \
  -H "Content-Type: application/json" \
  --data '{
  "organizationId": "abc123",
  "connectionId": "abc123",
  "dateFrom": "2026-05-14T05:40:30.820Z",
  "dateTo": "2026-05-14T05:40:30.820Z"
}'

Request body

  • organizationIdreq
    string
  • connectionIdreq
    string
  • dateFromreq
    string <date-time>
  • dateToreq
    string <date-time>

Response (200/201)

JSON object. The exact response shape is being progressively documented — call the endpoint with a real access token to see the live structure, or check the type definitions exposed by the official client libraries.

Error responses

400Validation
401Unauthenticated
403Forbidden
429RateLimit
500Internal
GET/api/expenses/export/csv#

Export expenses as CSV

scope · expenses:read

Streams the caller's expenses as a CSV file. Filter parameters scope the export the same way the list endpoint does.

Scope: expenses:read
Endpoint: GET /api/expenses/export/csv

Example request

curl "https://guliel.com/api/expenses/export/csv?organizationId=value" \
  -H "Authorization: Bearer $GULIEL_API_KEY"

Query parameters

  • organizationIdreq
    string
  • status
    string
  • source
    string
  • categoryId
    string
  • dateFrom
    string
  • dateTo
    string

Response (200/201)

JSON object. The exact response shape is being progressively documented — call the endpoint with a real access token to see the live structure, or check the type definitions exposed by the official client libraries.

Error responses

400Validation
401Unauthenticated
403Forbidden
500Internal
GET/api/expenses/export/manifest#

Export expenses as MANIFEST

scope · expenses:read

Streams the caller's expenses as a MANIFEST file. Filter parameters scope the export the same way the list endpoint does.

Scope: expenses:read
Endpoint: GET /api/expenses/export/manifest

Example request

curl "https://guliel.com/api/expenses/export/manifest?organizationId=value" \
  -H "Authorization: Bearer $GULIEL_API_KEY"

Query parameters

  • organizationIdreq
    string
  • ids
    string
  • status
    string
  • source
    string
  • categoryId
    string
  • dateFrom
    string
  • dateTo
    string

Response (200/201)

  • organizationreq
    object
    • idreq
      string
    • namereq
      string
  • generatedAtreq
    string
  • expensesreq
    array<object>
    • idreq
      string
    • datereq
      string
    • vendorreq
      string | null
    • titlereq
      string | null
    • categoryreq
      string | null
    • amountreq
      stringDecimal serialized as string to avoid float drift.
    • currencyreq
      string
    • taxAmountreq
      string | null
    • taxRatereq
      string | null
    • paymentMethodreq
      string | null
    • statusreq
      string
    • source
      string
    • notes
      string | null
    • attachments
      array<object>
      • idreq
        string
      • fileNamereq
        string
      • fileTypereq
        string | null
      • fileSizereq
        integer | null
    • createdBy
      object | null

Error responses

400Validation
401Unauthenticated
403Forbidden
500Internal
POST/api/expenses/scan#

Attach scan to an expense

scope · expenses:write

Creates a new scan attached to the given expense.

Scope: expenses:write
Endpoint: POST /api/expenses/scan

Example request

curl -X POST "https://guliel.com/api/expenses/scan" \
  -H "Authorization: Bearer $GULIEL_API_KEY" \
  -H "Content-Type: application/json" \
  --data '{
  "attachmentId": "abc123"
}'

Request body

  • attachmentIdreq
    string

Response (200/201)

  • expenseIdreq
    stringId of the resulting expense (may be the original or a merged sibling).
  • mergedIntoOrderreq
    object | nullWhen the scan matched an open supplier order, the scanned expense is merged into the order's existing expense.
  • orderTransition
    object | null
  • extractedDatareq
    objectRaw OCR / AI extraction output (vendor, line items, totals, etc.).
  • vendorMatch
    object | null
  • providerIdreq
    string
  • processingTimeMsreq
    number

Error responses

400Validation
401Unauthenticated
403Forbidden
429RateLimit
500Internal
POST/api/expenses/upload#

Attach upload to an expense

scope · expenses:write

Creates a new upload attached to the given expense.

Scope: expenses:write
Endpoint: POST /api/expenses/upload

Example request

curl -X POST "https://guliel.com/api/expenses/upload" \
  -H "Authorization: Bearer $GULIEL_API_KEY"

Request body

    Response (200/201)

    • idreq
      string
    • expenseIdreq
      string
    • fileNamereq
      string
    • fileTypereq
      string | null
    • fileSizereq
      integer | null
    • storageUrlreq
      string

    Error responses

    400Validation
    401Unauthenticated
    403Forbidden
    429RateLimit
    500Internal
    GET/api/expenses/usage#

    expense usage history

    scope · expenses:read

    Returns the caller's recent request log for the expense (last 7 days by default).

    Scope: expenses:read
    Endpoint: GET /api/expenses/usage

    Example request

    curl "https://guliel.com/api/expenses/usage?organizationId=value" \
      -H "Authorization: Bearer $GULIEL_API_KEY"

    Query parameters

    • organizationIdreq
      string

    Response (200/201)

    • scansUsedreq
      integer
    • scansAllowedreq
      integer
    • scansRemainingreq
      integer
    • resetAtreq
      string | nullISO timestamp for the next quota reset.
    • periodreq
      stringBilling period the counters cover, e.g. "2026-05".

    Error responses

    400Validation
    401Unauthenticated
    403Forbidden
    500Internal