Changelog

REST API

Updates to the REST API, newest first.

RSS feed

What’s new

Updates to the REST API and the MCP server, newest first.

  1. Version 1.0.0

    REST API 1.0.0

    Version 1.0.0 of the Filmo REST API.

    Added

    • retryAfter on the 429 rate_limited problem (REST and MCP): seconds to wait before retrying, the same value as the Retry-After header; detail says it too.
    • GET /planner/services rows carry rowKind (summary or detail): a service comes as one summary row (no assignee) plus one detail row per person or vehicle assigned, with the same orderServiceId. Group by orderServiceId; a null staffMemberId on the summary row does not mean uncovered.
    • Every successful response (2xx) of every operation carries an example: deterministic, fictitious data (RFC 2606 domains) that validates against the response schema.
    • One example response per error code (the 20 codes), as components.examples.Problem_<code> and referenced from the error responses of every operation, with the real title and shape of the problem.

    Changed

    • Error type URIs are now https://docs.filmo.io/errors/<code> (they were https://developers.filmo.io/errors/<code>). Branch on code, not on the URI.
    • Every 409 conflict now carries its cause in errors[0].code: in_progress, lock_timeout or invalid_state. Where errors[].code used to be conflict (unique violations, and the lines of an atomic batch that fail because their state forbids the operation) it is now invalid_state; a batch line that does not exist or has an invalid quantity still reports not_found or validation_error.
    • Success bodies are named schemas with an optional meta.warnings; write responses declare the Idempotent-Replay header.
    • The documentation moved to https://docs.filmo.io; in production /api/v1/docs redirects there.
    • Date filters (createdOn, issueDateBefore, returnTo…) are published as a single type: string with a pattern (a bare day YYYY-MM-DD or an ISO 8601 instant with offset) instead of anyOf of date and date-time, which some client generators could not compile. The server accepts exactly the same values.
    • The 401 and 403 responses no longer declare the RateLimit-* headers: they were never sent (those errors are answered before the request counts against the limit).
    • Every operation that can answer 402 for a missing add-on names it in its x-required-addon extension (e.g. ["incidence"]). ProblemDetails.requiredAddon and AccountDto.addons are now closed enums of add-on ids; GET /account only lists ids from that catalog.
    • The path of the errors[] entries of a 409 contact_in_use (the type of record that uses the contact: orders, projects, quotes, invoices, proformas, creditNotes, simpleCreditNotes, salesReceipts, supplierInvoices, contracts, subrents, purchaseOrders, sepaMandates, receipts, consignments), address_in_use (always orders) and insufficient_stock (items or spaces) is a closed set, published per code in the x-path-values extension of ProblemDetails.errors[].path.
    • Idempotency-Key is declared with minLength: 1. An empty key was already a 400 validation_error over REST; the MCP tools now reject an empty idempotencyKey the same way (errors[0].path = idempotencyKey) instead of ignoring it and running the write unprotected.

    Fixed

    • Every MCP tool result now validates with the official SDKs (Client.callTool in TypeScript and Python): successes with meta.idempotencyKey and every error used to fail with -32602 Structured content does not match the tool's output schema.
    • Detail reads sent with If-None-Match now answer 304 Not Modified (no body, with ETag) when the tag is current, as the spec already declared. Until now they returned a full 200.
    • Without Accept-Language, the 401s of a key that exists (revoked, expired, owner inactive or not an administrator) and the 403 insufficient_scope came back in Spanish whatever the owner of the key preferred. They now use the preferred language of the owner, like every later error; a request with no credential or an unknown key is still Spanish.
    • upsertExternalInvoice documented customer and totals problems as 422; they are 400 validation_error, and errors[0].path now names the field (contactId, contactTaxId, contactName, subTotal, taxTotal, total, lines[n].taxRate).

Was this page helpful?