Frihet Batch API
Batch creation of resources (up to 50 items per request).
Batch creation of resources (up to 50 items per request).
Every API here is available over the APIs.io API and to AI agents over MCP.
One button, every client — Claude, Cursor, VS Code and the rest.
https://apis.io/mcp
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.curl "https://apis.io/api/v1/apis/frihet-io:frihet-io-batch-api"
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.
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: 3.2.0
info:
title: Frihet Batch 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: Batch
description: Batch creation of resources (up to 50 items per request).
paths:
/v1/{resource}/batch:
post:
operationId: batchCreate
summary: Batch create resources
description: 'Create multiple resources in a single request. The request body must be
an array of items (max 50). Each item is validated independently.
Returns 201 if all succeed, 207 if partially successful, 400 if all fail.'
tags:
- Batch
parameters:
- name: resource
in: path
required: true
description: Resource type to batch create
schema:
type: string
enum:
- invoices
- expenses
- clients
- vendors
- products
- quotes
requestBody:
required: true
content:
application/json:
schema:
type: array
items:
type: object
minItems: 1
maxItems: 50
responses:
'201':
description: All items created successfully
content:
application/json:
schema:
$ref: '#/components/schemas/BatchResponse'
'207':
description: Partial success (some items failed validation)
content:
application/json:
schema:
$ref: '#/components/schemas/BatchResponse'
'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'
components:
responses:
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
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
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
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
schemas:
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
BatchResponse:
type: object
properties:
data:
type: array
items:
type: object
properties:
index:
type: integer
description: Index of the item in the original request array
success:
type: boolean
data:
type: object
description: Created resource (only when success is true)
error:
type: string
description: Error message (only when success is false)
details:
type: array
description: Validation error details (only on validation failure)
items:
type: object
summary:
type: object
properties:
total:
type: integer
description: Total items in request
example: 10
succeeded:
type: integer
description: Number of successfully created items
example: 8
failed:
type: integer
description: Number of failed items
example: 2
meta:
type: object
properties:
requestId:
type: string
timestamp:
type: string
format: date-time
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.
'