Frihet Deposits API

Manage client deposits and prepayments. Apply deposits against invoices or process refunds.

GitHub OpenAPI

Operations 8

GET /v1/deposits List deposits #
POST /v1/deposits Create a deposit #
GET /v1/deposits/{depositId} Get a deposit #
PUT /v1/deposits/{depositId} Full update a deposit #
PATCH /v1/deposits/{depositId} Partial update a deposit #
DELETE /v1/deposits/{depositId} Delete a deposit #
POST /v1/deposits/{depositId}/apply Apply deposit to an invoice #
POST /v1/deposits/{depositId}/refund Refund a deposit #

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/frihet-io:frihet-io-deposits-api"
All apis
curl "https://apis.io/api/v1/apis?limit=25"

Calls without X-API-Key need no key. Calls with it need a paid plan — get a key.

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

frihet-io-deposits-api-openapi.yml Raw ↑
openapi: 3.2.0
info:
  title: Frihet Deposits API
  version: '2026-03-18'
  description: 'The Frihet REST API provides programmatic access to your business data

    in Frihet, the AI-native business management platform for modern businesses. ## Authentication


    All API requests require an API key passed via the `X-API-Key` header. Generate keys from **Settings > API Keys** in the Frihet app. ```

    X-API-Key: fri_your_api_key_here

    ```


    ### API-key scopes


    A key without scopes (no list, or an empty one) is unrestricted. A key with a

    non-empty scope list gets only what the list grants. On such a key, every

    request other than `GET` and `HEAD` needs the `write` scope (or `*`), except

    the read-only `POST /v1/gestoria/aging` and the POS and purchase-order routes,

    which check their own `pos:` and `purchase-orders:` scopes. E-invoicing endpoints need an `einvoice:` scope,

    and the sends (`einvoice/export`, `face/submit`, `ticketbai/submit`) also need

    `write` or `einvoice:write`. A request without the scope it needs gets

    `403 Forbidden` with a `detail` naming that scope. The refusal happens before

    the idempotency gate, so it does not reserve the `Idempotency-Key`. `GET /v1/permissions/me` reports `denied.write`, `denied.einvoice` and

    `denied.einvoiceSend` for the calling key. Inventory (`/v1/inventory/*`) and purchase-order (`/v1/purchase-orders/*`) routes are

    not released yet: after the scope checks they answer `503` with code

    `INVENTORY_AUTHORITY_NOT_RELEASED`, before the idempotency gate. ## Rate Limiting


    The API enforces a rate limit of **100 requests per minute** per API key. When exceeded, the API returns `429 Too Many Requests` with an integer

    delta-seconds `Retry-After` header and matching `retryAfter` JSON field. Every authenticated response includes rate limit headers:


    | Header | Description |

    |--------|-------------|

    | `X-RateLimit-Limit` | Maximum requests per window (100) |

    | `X-RateLimit-Remaining` | Remaining requests in current window |

    | `X-RateLimit-Reset` | Unix timestamp (seconds) when the window resets |

    | `Retry-After` | Integer seconds until the window resets (429 responses) |


    ## Idempotency


    Most POST requests accept an `Idempotency-Key` header to prevent duplicate

    resource creation from network retries. For protected routes, the key is

    reserved against the operation *before* the request is executed, and it stays

    reserved for 24 hours unless released by an explicit pre-write refusal. ```

    Idempotency-Key: 550e8400-e29b-41d4-a716-446655440000

    ```


    Keys must be at most 64 characters. We recommend UUID v4. A retry of the same operation gets one of three answers:


    | Situation | Response |

    |---|---|

    | The first request finished | The **original status and body**, verbatim, with `X-Idempotent-Replayed: true` |

    | The first request is still running, or its outcome could not be recorded | `409 IDEMPOTENCY_REQUEST_IN_PROGRESS` |

    | The key is live but was used for a **different** operation | `409 IDEMPOTENCY_KEY_REUSED` — the second operation is not executed |


    The replayed status is the **stored** one. A `201` replays as `201`, not `200`. This includes stored error responses: a request that answered `4xx` or `5xx`

    replays that same status and body, it does not re-execute — except explicit

    pre-mutation refusals on `POST /v1/pos/cash-sessions/{id}/close` (`503` when

    the workspace POS money-authority grant is not enabled and `403 POS_INACTIVE`

    before the POS app is activated), which release the reservation instead of

    caching the refusal for 24 hours. ### When a retry is refused


    A `409 IDEMPOTENCY_REQUEST_IN_PROGRESS` after the first request already

    returned means the outcome could not be recorded — the response was too large

    or unserializable, or the bookkeeping write failed. The API refuses rather than

    execute a second time, because for a fiscal document a duplicate is not

    recoverable. **Reconcile; do not simply retry with a fresh key.** Read the resource back

    (`GET` the collection, or look for the document the operation would have

    created) and decide from its actual state.'
  contact:
    name: Frihet API Support
    url: https://docs.frihet.io/desarrolladores/api-rest
    email: api@frihet.io
  license:
    name: MIT
    url: https://opensource.org/licenses/MIT
  x-logo:
    url: https://frihet.io/logos/frihet-logo.svg
servers:
- url: https://api.frihet.io
  description: Production
security:
- ApiKeyAuth: []
tags:
- name: Deposits
  description: Manage client deposits and prepayments. Apply deposits against invoices or process refunds.
paths:
  /v1/deposits:
    get:
      operationId: listDeposits
      summary: List deposits
      description: Retrieve a paginated list of deposits with optional date range, client, and search filters.
      tags:
      - Deposits
      parameters:
      - $ref: '#/components/parameters/Limit'
      - $ref: '#/components/parameters/Offset'
      - $ref: '#/components/parameters/Cursor'
      - $ref: '#/components/parameters/From'
      - $ref: '#/components/parameters/To'
      - $ref: '#/components/parameters/Search'
      - $ref: '#/components/parameters/Fields'
      - $ref: '#/components/parameters/FilterClientId'
      responses:
        '200':
          description: A paginated list of deposits
          content:
            application/json:
              schema:
                allOf:
                - $ref: '#/components/schemas/PaginatedResponse'
                - type: object
                  properties:
                    data:
                      type: array
                      items:
                        $ref: '#/components/schemas/Deposit'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '429':
          $ref: '#/components/responses/RateLimited'
        '500':
          $ref: '#/components/responses/InternalError'
    post:
      operationId: createDeposit
      summary: Create a deposit
      description: Record a new client deposit or prepayment.
      tags:
      - Deposits
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/DepositCreate'
      responses:
        '201':
          description: Deposit created
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Deposit'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/WriteScopeForbidden'
        '429':
          $ref: '#/components/responses/RateLimited'
        '500':
          $ref: '#/components/responses/InternalError'
  /v1/deposits/{depositId}:
    get:
      operationId: getDeposit
      summary: Get a deposit
      description: Retrieve a single deposit by ID.
      tags:
      - Deposits
      parameters:
      - $ref: '#/components/parameters/DepositId'
      - $ref: '#/components/parameters/Fields'
      responses:
        '200':
          description: Deposit details
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Deposit'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
        '429':
          $ref: '#/components/responses/RateLimited'
        '500':
          $ref: '#/components/responses/InternalError'
    put:
      operationId: updateDeposit
      summary: Full update a deposit
      description: Replace all fields on an existing deposit.
      tags:
      - Deposits
      parameters:
      - $ref: '#/components/parameters/DepositId'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/DepositCreate'
      responses:
        '200':
          description: Deposit updated
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Deposit'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/WriteScopeForbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '429':
          $ref: '#/components/responses/RateLimited'
        '500':
          $ref: '#/components/responses/InternalError'
    patch:
      operationId: patchDeposit
      summary: Partial update a deposit
      description: Update only the provided fields on an existing deposit.
      tags:
      - Deposits
      parameters:
      - $ref: '#/components/parameters/DepositId'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/DepositCreate'
      responses:
        '200':
          description: Deposit updated
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Deposit'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/WriteScopeForbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '429':
          $ref: '#/components/responses/RateLimited'
        '500':
          $ref: '#/components/responses/InternalError'
    delete:
      operationId: deleteDeposit
      summary: Delete a deposit
      description: Permanently delete a deposit record.
      tags:
      - Deposits
      parameters:
      - $ref: '#/components/parameters/DepositId'
      responses:
        '204':
          description: Deposit deleted
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/WriteScopeForbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '429':
          $ref: '#/components/responses/RateLimited'
        '500':
          $ref: '#/components/responses/InternalError'
  /v1/deposits/{depositId}/apply:
    post:
      operationId: applyDeposit
      summary: Apply deposit to an invoice
      description: 'Apply all or part of a deposit''s remaining balance to a specific invoice.

        The deposit balance and the invoice''s deposit settlement are written in one

        transaction, so an application and a refund racing on the same balance can

        never both succeed. Returns an error if the requested amount exceeds the

        remaining balance (refunds already reduce it), if the deposit has already

        been applied to the given invoice, or if the deposit or invoice does not exist.'
      tags:
      - Deposits
      parameters:
      - $ref: '#/components/parameters/DepositId'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
              - invoiceId
              - invoiceNumber
              - amount
              properties:
                invoiceId:
                  type: string
                  description: ID of the invoice to apply the deposit to
                  example: abc123def456
                invoiceNumber:
                  type: string
                  description: Invoice number (for audit trail)
                  example: FAC-2026-0042
                amount:
                  type: number
                  description: Amount to apply (must be positive and not exceed remaining balance)
                  example: 500
      responses:
        '200':
          description: Deposit applied successfully
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    example: true
                  depositId:
                    type: string
                    example: dep_abc123
                  appliedAmount:
                    type: number
                    example: 500
                  remainingBalance:
                    type: number
                    example: 250
                  status:
                    type: string
                    enum:
                    - active
                    - partially_applied
                    - fully_applied
                    example: partially_applied
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/WriteScopeForbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '409':
          description: 'INVOICE_PAYMENT_CAPTURE_FENCED: a portal payment capture is in progress

            on the invoice; retry after it settles. Nothing is written.

            '
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '429':
          $ref: '#/components/responses/RateLimited'
        '500':
          $ref: '#/components/responses/InternalError'
  /v1/deposits/{depositId}/refund:
    post:
      operationId: refundDeposit
      summary: Refund a deposit
      description: 'Refund all or part of a deposit''s remaining balance to the client.

        If `amount` is omitted, the full remaining balance is refunded.

        The refund is decided on the balance read in the same transaction that

        writes it, so it can never also spend a balance a concurrent application

        consumed. Returns an error if the deposit is already fully refunded, has no

        remaining balance, or if the requested amount exceeds the remaining balance.'
      tags:
      - Deposits
      parameters:
      - $ref: '#/components/parameters/DepositId'
      requestBody:
        content:
          application/json:
            schema:
              type: object
              properties:
                amount:
                  type: number
                  description: Amount to refund. Omit to refund the full remaining balance.
                  example: 250
      responses:
        '200':
          description: Deposit refunded successfully
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    example: true
                  depositId:
                    type: string
                    example: dep_abc123
                  refundedAmount:
                    type: number
                    example: 250
                  remainingBalance:
                    type: number
                    example: 0
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/WriteScopeForbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '429':
          $ref: '#/components/responses/RateLimited'
        '500':
          $ref: '#/components/responses/InternalError'
components:
  parameters:
    Search:
      name: q
      in: query
      description: 'Full-text search query. Searches across relevant fields per resource type

        (e.g., clientName, documentNumber for invoices; name, email for clients).

        Case-insensitive.

        '
      schema:
        type: string
        example: acme
    Offset:
      name: offset
      in: query
      description: Number of items to skip for pagination (max 10000)
      schema:
        type: integer
        minimum: 0
        maximum: 10000
        default: 0
    FilterClientId:
      name: clientId
      in: query
      description: Filter invoices or quotes by client ID
      schema:
        type: string
    To:
      name: to
      in: query
      description: End date filter in ISO format (YYYY-MM-DD)
      schema:
        type: string
        format: date
        example: '2026-12-31'
    From:
      name: from
      in: query
      description: Start date filter in ISO format (YYYY-MM-DD)
      schema:
        type: string
        format: date
        example: '2026-01-01'
    Limit:
      name: limit
      in: query
      description: Number of items to return (default 50, max 100)
      schema:
        type: integer
        minimum: 1
        maximum: 100
        default: 50
    DepositId:
      name: depositId
      in: path
      required: true
      description: Deposit unique identifier
      schema:
        type: string
    Cursor:
      name: cursor
      in: query
      description: 'Cursor for cursor-based pagination. Use the `nextCursor` value from

        a previous response to fetch the next page. When provided, `offset`

        is ignored. The cursor is a base64url-encoded JSON string.

        '
      schema:
        type: string
        example: eyJpc3N1ZURhdGUiOiIyMDI2LTAzLTIwIiwiX19pZCI6ImFiYzEyMyJ9
    Fields:
      name: fields
      in: query
      description: 'Comma-separated list of fields to include in the response.

        The `id` field is always included. Example: `fields=id,name,total`

        '
      schema:
        type: string
        example: id,name,total
  schemas:
    DepositCreate:
      type: object
      required:
      - clientId
      - clientName
      - amount
      - description
      - receivedDate
      properties:
        clientId:
          type: string
          description: ID of the client who made the deposit
          example: client_abc123
        clientName:
          type: string
          description: Client name (denormalized for display)
          example: Acme Corp
        amount:
          type: number
          description: Deposit amount (must be positive)
          example: 750
        currency:
          type: string
          description: Currency code (ISO 4217)
          default: EUR
          example: EUR
        description:
          type: string
          description: Description of the deposit
          example: Prepayment for Q2 project
        receivedDate:
          type: string
          format: date
          description: Date the deposit was received (YYYY-MM-DD)
          example: '2026-04-01'
        paymentMethod:
          type: string
          description: Payment method used
          example: bank_transfer
        paymentReference:
          type: string
          description: Payment reference or transaction ID
          example: TRF-2026-0042
    Error:
      type: object
      required:
      - error
      properties:
        error:
          type: string
          description: Human-readable error message
        code:
          type: string
          description: Optional machine-readable code, including MISSING_FISCAL_INFO, AEAT_QR_UNAVAILABLE (422) and AEAT_RECORD_IRREPRODUCIBLE (409) on invoice PDF.
        message:
          type: string
          description: Optional explanatory message accompanying the error code.
        details:
          type: array
          description: Validation error details (Zod issues). Only present on 400 validation errors.
          items:
            type: object
            properties:
              code:
                type: string
              message:
                type: string
              path:
                type: array
                items:
                  type: string
        meta:
          type: object
          properties:
            requestId:
              type: string
              description: Unique request identifier for tracing
    PaginatedResponse:
      type: object
      properties:
        data:
          type: array
          items: {}
        total:
          type: integer
          description: Total number of items matching the query
        limit:
          type: integer
          description: Number of items per page
        offset:
          type: integer
          description: Current offset
        nextCursor:
          type: string
          description: 'Cursor for fetching the next page of results. Only present when there

            are more results available. Pass this value as the `cursor` query parameter

            in the next request. Base64url-encoded.

            '
          example: eyJpc3N1ZURhdGUiOiIyMDI2LTAzLTIwIiwiX19pZCI6ImFiYzEyMyJ9
        truncated:
          type: boolean
          description: 'Only present (and `true`) when the underlying search query

            saturated the internal 500-document cap. `total` therefore reports

            the size of the first 500 matches, not the full match count.

            Narrow the search query (taxId / fiscal id, exact email) to ensure

            you see every result.

            '
          example: true
    DepositStatus:
      type: string
      enum:
      - active
      - partially_applied
      - fully_applied
      - refunded
    Deposit:
      allOf:
      - type: object
        properties:
          id:
            type: string
            description: Unique identifier
            example: dep_abc123
          createdAt:
            type: string
            format: date-time
          updatedAt:
            type: string
            format: date-time
      - $ref: '#/components/schemas/DepositCreate'
      - type: object
        properties:
          status:
            $ref: '#/components/schemas/DepositStatus'
          appliedAmount:
            type: number
            description: Total amount already applied to invoices
            example: 250
          remainingBalance:
            type: number
            description: Remaining balance available for application or refund
            example: 500
          refundedAmount:
            type: number
            description: Total amount refunded to the client
            example: 0
          applications:
            type: array
            description: List of invoice applications for this deposit
            items:
              type: object
              properties:
                invoiceId:
                  type: string
                invoiceNumber:
                  type: string
                amount:
                  type: number
                appliedAt:
                  type: string
                  format: date
  responses:
    InternalError:
      description: Internal server error
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
          example:
            error: Internal server error
            meta:
              requestId: 550e8400-e29b-41d4-a716-446655440000
    BadRequest:
      description: Bad request - invalid parameters or request body
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
          example:
            error: Validation error
            details:
            - code: invalid_type
              message: Required
              path:
              - clientName
            meta:
              requestId: 550e8400-e29b-41d4-a716-446655440000
    NotFound:
      description: Resource not found
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
          example:
            error: Resource not found
            meta:
              requestId: 550e8400-e29b-41d4-a716-446655440000
    Unauthorized:
      description: Authentication required or invalid API key
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
          example:
            error: Invalid or expired API key
            meta:
              requestId: 550e8400-e29b-41d4-a716-446655440000
    RateLimited:
      description: Rate limit exceeded (100 requests per minute)
      headers:
        Retry-After:
          description: Integer seconds until the current rate-limit window resets
          schema:
            type: integer
            minimum: 1
          example: 60
        X-RateLimit-Reset:
          $ref: '#/components/headers/X-RateLimit-Reset'
      content:
        application/json:
          schema:
            type: object
            properties:
              error:
                type: string
                example: Rate limit exceeded
              message:
                type: string
                example: Maximum 100 requests per minute
              retryAfter:
                type: integer
                description: Seconds until rate limit resets
                example: 60
              meta:
                type: object
                properties:
                  requestId:
                    type: string
    WriteScopeForbidden:
      description: Write scope required to mutate, send or submit. The API key must include the `write` scope (or `einvoice:write` for e-invoice sends) or be unrestricted.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
          example:
            error: Forbidden
            detail: This API key does not have the write scope required to create, update, delete, send or submit. E-invoice sends also accept einvoice:write. Use an API key with write access.
            meta:
              requestId: 550e8400-e29b-41d4-a716-446655440000
  headers:
    X-RateLimit-Reset:
      description: Unix timestamp (seconds) when the rate limit window resets
      schema:
        type: integer
        example: 1742310060
  securitySchemes:
    ApiKeyAuth:
      type: apiKey
      in: header
      name: X-API-Key
      description: 'API key for authentication. Keys start with `fri_` prefix.

        Generate keys from Settings > API Keys in the Frihet app.

        '