Desarrolladores

Construye sobre Fina en minutos

Acepta pagos con la API de Fina Pay, crea tus propias apps de clientes sobre negocios de Fina y deja que tu asistente de IA escriba la integración por ti.

API v1 · estable9 APIs · 546 endpointsOpenAPI 3.1

Integra con tu asistente de IA

Claude Code, Cursor, Copilot, ChatGPT y más

Pega un prompt y tu asistente lee toda la documentación y el OpenAPI antes de escribir código.

Servidor MCP de la documentación

Empezar

Overview

What you can build on Fina, which API to use and how to authenticate.

Fina is the operating system for small businesses in the Dominican Republic: books, a wallet (Fina Pay) and vertical modules (farm, store, restaurant, barbershop, real estate, car rental). Everything a third party can use is a plain HTTPS + JSON API described by an OpenAPI 3.1 spec.

I want to…UseCredential
Accept payments in my store, app or backendFina Pay APISecret key sk_live_…
Build my own customer app or website for a Fina businessStorefront APIsApp key of the module
Build a kitchen display / POS for my restaurantRestaurant staff APIPaired device + staff PIN
Automate AI media jobs with my creditsVideo Studio APIPersonal key fina_vs_…

Conventions shared by every API

  • Money is always an integer in minor units (amount_minor, price_minor…): 150000 = RD$1,500.00. Never a float.
  • Timestamps are ISO 8601 in UTC (2026-10-05T14:00:00.000Z). Business dates (YYYY-MM-DD) are in Dominican Republic time (UTC−4).
  • Requests and responses are application/json. Error codes are stable strings — branch on them, never on the message.
  • Base host: https://fina.do. Every API is versioned in its path (/v1); breaking changes ship as a new version.

Built for AI assistants

Every page of these docs is also Markdown (append .md to its URL). llms.txt indexes them, llms-full.txt has all of it in one file, and a read-only MCP server lets Claude Code, Cursor and other assistants search the docs and read the OpenAPI specs.

Empezar

Quickstart

Create an API key, take your first payment and receive the webhook in about five minutes.

1. Create your API keys

  1. Activate Fina Pay on your personal account in /app/wallet.
  2. Open your business panel, edit your business and go to the Connectors tab → Fina Pay.
  3. Create the connection: pick the income account and category where paid charges are booked.
  4. Copy the public key (pk_live_…) and the secret key (sk_live_…). The secret is shown only once — store it as FINA_SECRET_KEY in your server environment.
  5. Optional but recommended: set a webhook URL (HTTPS) and an IPN secret. Store the secret as FINA_IPN_SECRET.
The secret key moves money. Keep it on your server only — never in a browser, a mobile app or a Git repository. If it leaks, regenerate it from the same connector.

2. Create your first charge

POST /charges
curl -X POST https://fina.do/api/wallet/v1/charges \
  -H "Authorization: Bearer $FINA_SECRET_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "amount_minor": 150000,
    "description": "Order #42",
    "reference": "order-42",
    "metadata": { "order_id": "42" }
  }'
201 Created
{
  "id": "550e8400-e29b-41d4-a716-446655440000",
  "status": "pending",
  "payment_url": "https://fina.do/pay/c/3f9c…",
  "amount_minor": 150000,
  "currency_code": "DOP",
  "reference": "order-42",
  "expires_at": "2026-10-06T14:00:00.000Z"
}

Redirect the customer to payment_url. They sign in to Fina (or open the Fina app through the universal link), confirm with their wallet PIN and pay from their balance.

3. Receive the webhook

When the charge is paid, Fina sends POST to your webhook URL with the event in the body and an x-fina-sig header. Verify the signature, then fulfil the order.

Webhook handler
// Express. Any framework works: you need the raw body and the x-fina-sig header.
import express from 'express'
import { verifyFinaSignature } from './fina'

const app = express()

app.post('/webhooks/fina', express.text({ type: 'application/json' }), async (req, res) => {
  if (!verifyFinaSignature(req.body, req.get('x-fina-sig') ?? '', process.env.FINA_IPN_SECRET!)) {
    return res.status(401).end()
  }
  const event = JSON.parse(req.body)
  if (event.event === 'charge.paid') {
    // Idempotent: a retried delivery must not ship the order twice.
    await markOrderPaid({ reference: event.reference, chargeId: event.charge_id, netMinor: event.net_minor })
  }
  res.status(200).end() // answer 2xx fast; do slow work in a queue
})

The verifyFinaSignature helper is in Webhooks. No webhook URL yet? Poll GET /charges/{id} until status is paid.

That is the whole integration. Next: read Charges for statuses, fees and refunds, and Testing before going live.

Empezar

Build with AI

llms.txt, Markdown pages, a ready-made prompt and a read-only MCP server for Claude Code, Cursor and other assistants.

These docs are designed to be read by your AI coding assistant as well as by you. Pick whichever fits your tool.

Paste this prompt

Works in Claude Code, Cursor, Windsurf, GitHub Copilot, ChatGPT or Claude.ai. Replace the last line with your task.

Prompt
I'm integrating Fina (https://fina.do) into my project.
Before writing any code, read the full developer docs: https://fina.do/llms-full.txt
Use the OpenAPI spec as the source of truth for request and response shapes: https://fina.do/api/wallet/v1/openapi.json (Fina Pay).
Storefront and other APIs list their own spec in https://fina.do/llms.txt.

Rules:
- Keep the secret key (sk_live_…) on the server, read it from the FINA_SECRET_KEY env var, never commit it.
- Amounts are integers in minor units (centavos). Never use floats for money.
- Use my order id as `reference` so retries never create duplicate charges.
- Verify the x-fina-sig webhook signature exactly as the docs describe, and make the webhook handler idempotent.

Task: <describe what you want, e.g. "add a Pay with Fina button to checkout and mark the order paid on charge.paid">

Connect the docs MCP server

A read-only Model Context Protocol server at https://fina.do/api/developers/mcp (Streamable HTTP, no auth). Tools: search_docs, get_doc_page, list_apis, list_endpoints, get_endpoint and get_openapi_spec. It only reads documentation — it never touches your account or your money.

Install
claude mcp add --transport http fina-docs https://fina.do/api/developers/mcp

LLM-friendly files

URLWhat it is
/llms.txtIndex in the llmstxt.org format: one link per page, to its Markdown version.
/llms-full.txtAll the documentation in one Markdown file, endpoint tables included.
/developers/<page>.mdAny single page as Markdown, e.g. /developers/webhooks.md.
…/openapi.jsonThe machine-readable contract of each API (see API reference).

Every page also has Copy as Markdown, Open in Claude and Open in ChatGPT buttons.

Example: an agent that creates Fina Pay charges

If you build your own assistant with the Claude API, give it a tool that calls Fina Pay. Official Anthropic SDK, tool runner, model claude-opus-5-5. The tool only creates charges; the customer still confirms every payment in Fina with their PIN.

Claude + Fina Pay
// npm i @anthropic-ai/sdk zod
import Anthropic from '@anthropic-ai/sdk'
import { betaZodTool } from '@anthropic-ai/sdk/helpers/beta/zod'
import { z } from 'zod'

const client = new Anthropic() // reads ANTHROPIC_API_KEY

const createFinaCharge = betaZodTool({
  name: 'create_fina_charge',
  description: 'Create a Fina Pay charge and return the payment URL the customer must open to pay.',
  inputSchema: z.object({
    amount_minor: z.number().int().positive().describe('Amount in centavos (RD$1.00 = 100)'),
    description: z.string(),
    reference: z.string().describe('Unique order id; makes retries idempotent'),
  }),
  run: async (input) => {
    const res = await fetch('https://fina.do/api/wallet/v1/charges', {
      method: 'POST',
      headers: { Authorization: `Bearer ${process.env.FINA_SECRET_KEY}`, 'Content-Type': 'application/json' },
      body: JSON.stringify(input),
    })
    return JSON.stringify(await res.json())
  },
})

const finalMessage = await client.beta.messages.toolRunner({
  model: 'claude-opus-5-5',
  max_tokens: 16000,
  tools: [createFinaCharge],
  messages: [{ role: 'user', content: 'Charge RD$1,500 for order #42 (2 bags of coffee) and give me the link.' }],
})

console.log(finalMessage.content)

Fina Pay

Authentication

Public and secret keys of the Fina Pay connector, rate limits and key rotation.

Each Fina Pay connection (one per integration, under your business → Connectors → Fina Pay) has a key pair:

KeyFormatWhereUsed for
Secret keysk_live_…Server onlyAuthorization: Bearer sk_live_… on every charge, payout and recipient endpoint
Public keypk_live_…Safe in a browserPublic lookup GET /charges?token=…&public_key=… for your own payment page
IPN secretYou choose itServer onlyVerifying the x-fina-sig header of webhooks
Header
Authorization: Bearer sk_live_…
  • Fina stores only a hash of the secret key: it is shown once. Regenerating it invalidates the old one immediately.
  • Pausing or deleting the connection makes its keys answer 401 unauthorized.
  • Rate limit: 60 requests per minute per connection. Above that you get 429 rate_limited; wait and retry with backoff.
  • Each connection books paid charges into the income account and category you chose, so use one connection per sales channel if you want separate reporting.

Fina Pay

Charges

Lifecycle of a Fina Pay charge: statuses, expiry, fees, cancellation and refunds.

Lifecycle

  1. Your backend calls POST /charges. The charge is pending and has a payment_url.
  2. The customer opens payment_url, signs in to Fina and confirms with their wallet PIN.
  3. The charge becomes paid; you receive charge.paid and GET /charges/{id} shows paid_at, fee_minor and net_minor.
  4. Unpaid charges become expired at expires_at (24 h by default, expires_in_seconds to change it), or cancelled if you call POST /charges/{id}/cancel.
StatusMeaningWebhook
pendingWaiting for the customer—
paidMoney is in your Fina Pay walletcharge.paid
expiredNot paid before expires_atcharge.expired
cancelledYou cancelled it while pendingcharge.cancelled
refundedA paid charge was returned to the customer (fully or partly)charge.refunded

Fees

The customer always pays exactly amount_minor. Fina keeps the collection fee of your plan from what you receive: once paid, fee_minor is the fee and net_minor = amount_minor − fee_minor is what lands in your wallet. Before payment both are null.

Idempotency

reference is unique per connection. Calling POST /charges again with the same reference returns the existing charge (same id, same payment_url) instead of charging twice — so you can safely retry on timeouts. Use your order id.

Refunds

Refunds are issued from Fina (for example when a business rejects an order, cancels a table deposit or a subscription) and return the money — including the proportional fee — to the customer's wallet. There is no public refund endpoint yet; you are notified with charge.refunded.

Get and cancel

GET /charges/{id}
curl https://fina.do/api/wallet/v1/charges/CHARGE_ID \
  -H "Authorization: Bearer $FINA_SECRET_KEY"
POST /charges/{id}/cancel
curl -X POST https://fina.do/api/wallet/v1/charges/CHARGE_ID/cancel \
  -H "Authorization: Bearer $FINA_SECRET_KEY"

Full request and response schemas: Fina Pay API reference.

Fina Pay

Payouts

Pay Fina Pay users from your own wallet: withdrawals, prizes, refunds outside a charge. Idempotent and instant.

A payout moves money from the Fina Pay wallet of the connection owner to another Fina user, in one atomic step. Fina does not create money: if your wallet does not have the balance, the payout is rejected. Use it to pay your users' withdrawals, prizes or anything you owe them.

Payouts are off by default. The business owner turns on Payouts via API in the Fina Pay connector (business → Connectors → Fina Pay). While it is on, anyone with the secret key can send money from that wallet: keep the key on your server only.

Flow

  1. Your user enters their Fina Pay account number (10 digits) or the phone linked to their wallet.
  2. Call GET /recipients?account=… and show them the name the money will go to, so they can confirm it is them.
  3. Call POST /payouts with your withdrawal id as reference. A 201 means the money is already in their wallet; they get a notification.
  4. Store the payout id. GET /payouts/{id} returns it again at any time.
GET /recipients
curl "https://fina.do/api/wallet/v1/recipients?account=8095551234" \
  -H "Authorization: Bearer $FINA_SECRET_KEY"
POST /payouts
curl -X POST https://fina.do/api/wallet/v1/payouts \
  -H "Authorization: Bearer $FINA_SECRET_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "account": "2345678901",
    "amount_minor": 250000,
    "reference": "withdrawal-981",
    "description": "Retiro de tu saldo"
  }'
FieldRules
accountRequired. Account number or phone, the same values a Fina user can send money to.
amount_minorRequired. Integer > 0 in centavos. Debited from your wallet as is: there is no payout fee.
referenceRequired, up to 64 characters, unique per connection.
descriptionOptional, up to 140 characters. The recipient sees it.
metadataOptional JSON object (up to 4 KB), stored with the payout.

Idempotency

reference is unique per connection. Sending POST /payouts again with a reference that was already paid answers 200 with the original payout and duplicate: true — even if the body differs and even with simultaneous requests. Nothing is paid twice, so retry freely on timeouts with the same reference.

Errors

HTTPCodeMeaning
400invalid_json, invalid_amount, reference_required, invalid_reference, invalid_description, invalid_metadataFix the request.
400same_accountThe recipient is the wallet of the connection owner.
403payouts_disabledPayouts via API are off for this connection.
404recipient_not_foundNo active Fina Pay wallet with that account number or phone.
409insufficient_fundsYour wallet balance is lower than amount_minor. Nothing moved.
409currency_mismatchYour wallet and the recipient's use different currencies.
409wallet_blockedYour wallet is blocked. Contact Fina support.

Full request and response schemas: Fina Pay API reference.

Fina Pay

Webhooks

Signed IPN events (charge.* and subscription.*), signature verification in 3 languages, retries and idempotent handling.

Set a webhook URL and an IPN secret on your connection. Fina then sends POST requests with Content-Type: application/json and the header x-fina-sig for each event.

Charge events

  • charge.paid
  • charge.expired
  • charge.cancelled
  • charge.refunded
charge.paid
{
  "event": "charge.paid",
  "charge_id": "550e8400-e29b-41d4-a716-446655440000",
  "reference": "order-42",
  "amount_minor": 150000,
  "fee_minor": 3750,
  "net_minor": 146250,
  "currency_code": "DOP",
  "status": "paid",
  "transfer_id": "8d2b…",
  "paid_at": "2026-10-05T14:03:11.000Z",
  "timestamp": "2026-10-05T14:03:12.000Z"
}

Verify the signature

x-fina-sig is the hex HMAC-SHA512 of the payload serialized as JSON.stringify(payload, Object.keys(payload).sort()) — compact JSON whose keys follow the sorted list of top-level keys — keyed with your IPN secret. Note that it is computed over that re-serialization, not over the raw body bytes: parse the body, rebuild the string and compare in constant time. The Python and PHP helpers below produce exactly the same bytes as the JavaScript call.

verifyFinaSignature
import { createHmac, timingSafeEqual } from 'node:crypto'

/** Fina signs JSON.stringify(payload, sortedTopLevelKeys) with HMAC-SHA512. */
export function verifyFinaSignature(rawBody: string, signature: string, secret: string): boolean {
  const payload = JSON.parse(rawBody)
  const signed = JSON.stringify(payload, Object.keys(payload).sort())
  const expected = createHmac('sha512', secret).update(signed).digest('hex')
  return expected.length === signature.length && timingSafeEqual(Buffer.from(expected), Buffer.from(signature))
}

Delivery and retries

  • Answer any 2xx within 15 seconds to acknowledge. Do slow work (emails, fulfilment) after responding.
  • Anything else (timeout, 4xx, 5xx, network error) is retried after 1 min, 5 min, 30 min, 2 h and 24 h; after that the delivery is marked failed.
  • You can see recent deliveries and their status in the Fina Pay connector of your business.
  • Events can arrive more than once and out of order. Make the handler idempotent (key on charge_id + event, or event_id for subscriptions) and, when in doubt, read the current state with GET /charges/{id}.

Subscription events

Merchants using Fina Pay subscriptions also receive subscription.created, subscription.trial_will_end, subscription.renewed, subscription.payment_failed, subscription.past_due, subscription.cancel_scheduled, subscription.reactivated, subscription.paused, subscription.resumed, subscription.plan_changed, subscription.canceled, subscription.expired and mandate.revoked, with the same header, signature and retries. See Subscriptions.

Fina Pay

Subscriptions

Recurring Fina Pay payments: hosted checkout, customer portal and lifecycle webhooks (rolling out).

Fina Pay subscriptions are rolling out by business. Subscription webhooks to external URLs are enabled per platform setting; until then, plans are managed from Fina and the modules that use them (for example farm subscription boxes).

A subscription is a mandate the customer authorizes once with their wallet PIN; Fina then charges each period automatically from their Fina Pay balance.

  • Hosted checkout — https://fina.do/pay/s/<token> shows the plan terms, signs the customer in and asks for the PIN. It opens the Fina app when installed. Supports a ?return= URL back to your site.
  • Customer portal — https://fina.do/pay/subscriptions, where customers see, cancel or reactivate their subscriptions and set a spending cap.
  • Entitlement — every webhook carries entitled and entitled_until: give access while entitled is true. Grace periods and trials are already applied.
subscription.renewed
{
  "event": "subscription.renewed",
  "event_id": "0b6f…",
  "subscription_id": "a1c2…",
  "plan_code": "coffee-monthly",
  "status": "active",
  "cancel_at_period_end": false,
  "entitled": true,
  "entitled_until": "2026-11-05T14:00:00.000Z",
  "period": { "id": "…", "sequence": 2, "kind": "paid", "start": "…", "end": "…", "status": "paid", "amount_minor": 90000, "merchant_ref": null },
  "charge": { "id": "…", "amount_minor": 90000, "fee_minor": 2250, "net_minor": 87750, "paid_at": "…" },
  "external_customer_ref": "customer-981",
  "timestamp": "2026-10-05T14:00:02.000Z"
}
Signature caveat for subscription events: because the signed string uses the top-level key list as a JSON.stringify replacer, nested objects (period, charge) contribute only keys that also exist at the top level. Use the helpers in Webhooks as-is — they reproduce this exactly — and re-read sensitive nested values from Fina when they drive money decisions.

Apps

Storefront APIs

Build your own customer app or website on top of a Fina business: app key, envelope, /config, delivery quotes and weight-based shipping.

The official white-label apps of Fina businesses are built on these APIs, and you can use them too. Each API is scoped to one business: the app key identifies it, and no endpoint lists or discovers other businesses.

VerticalBase pathApp key header
Farm storefront API/api/farm/v1X-Farm-App-Key
Boutique storefront API/api/boutique/v1X-Boutique-App-Key
Restaurant storefront API/api/restaurant/v1X-Restaurant-App-Key
Barbershop booking API/api/barbershop/v1X-Barbershop-App-Key
Real estate API/api/real-estate/v1X-Real-Estate-App-Key
Rent a car API/api/rent-car/v1X-Rent-Car-App-Key

Authentication

  • App key — created by the business owner in their panel, in the module's customer-app section. It is an identifier and rate-limit scope, not a secret: it ships inside the app.
  • Customer token — private routes (/me, /orders, /addresses…) also need Authorization: Bearer <access token> of the customer's Fina account (Supabase Auth JWT). Public routes (/config, /catalog, /windows, slots…) work with the app key alone.

Response envelope

Farm, boutique, restaurant and barbershop answer { "data": …, "error": null, "request_id": "…" }; on failure data is null and error is { "code", "message", "details"? } with a stable upper-case code (RATE_LIMITED, OUT_OF_DELIVERY_ZONE, SHIPMENT_LIMIT_EXCEEDED…). Every response has an X-Request-Id header — include it when contacting support.

First call
curl https://fina.do/api/boutique/v1/config \
  -H "X-Boutique-App-Key: $APP_KEY"

Start with /config

GET /config returns the business identity and everything the app needs to boot: fulfilment modes, delivery pricing (delivery_fee_by_order_total, delivery_fee_by_weight with its weight_unit), payment options (card_enabled, cash_on_delivery) and whether Fina Pay subscriptions are available. Cache it and refresh on app start.

Delivery quotes and weight-based shipping

POST /delivery/estimate with an address returns fee_minor, distance_meters, the delivery zone and pricing_mode (flat, per_km, tiers, order_total, by_weight). For weight-based shipping send the cart as items: [{ product_id, variant_id?, quantity }]: the response adds weight (real, volumetric and billable grams), fee_by_weight and the zone limits. If the cart exceeds every zone's maximum weight or size you get deliverable: false with reason: "too_heavy" | "too_large", and creating the order answers 409 SHIPMENT_LIMIT_EXCEEDED.

Rate limits

Per-minute limits per app key and client. Over the limit: 429 with error.code = "RATE_LIMITED" and details.retry_after_seconds.

Referencia de la API

Fina Pay API

Charges, payment links, payouts and signed webhooks for any store, app or backend.

Charges, payment links, payouts and signed webhooks for any store, app or backend.

Base URLhttps://fina.do/api/wallet/v1
AuthenticationBearer secret key (sk_live_…) from the Fina Pay connector of your business
OpenAPI 3.1/api/wallet/v1/openapi.json
Built forMerchants and developers integrating payments

Webhooks are part of the spec too (webhooks in OpenAPI 3.1). Guides: Quickstart, Charges, Webhooks.

Endpoints

7 endpointshttps://fina.do/api/wallet/v1
  • GET/charges/{id}Secret key

    Get a charge (poll its status, fee and net)

  • GET/payouts/{id}Secret key

    Get a payout of this connection

Referencia de la API

Farm storefront API

Catalog, delivery windows, orders, subscription boxes and delivery quotes of one farm.

Catalog, delivery windows, orders, subscription boxes and delivery quotes of one farm.

Base URLhttps://fina.do/api/farm/v1
AuthenticationX-Farm-App-Key + customer bearer token on private routes
OpenAPI 3.1/api/farm/v1/openapi.json
Built forCustom customer apps and websites for a farm

Shared rules (app key, envelope, /config, delivery quotes): Storefront APIs.

Endpoints

76 endpointshttps://fina.do/api/farm/v1
  • GET/configApp key

    Identidad de la granja y lo que la app necesita para arrancar

  • GET/locationsApp key

    Sucursales activas donde se puede recoger

  • GET/catalogApp key

    Catálogo con unidad de venta y precio por pieza derivado

  • GET/plansApp key

    Planes con su precio, su ahorro por ciclo y por pieza

  • GET/windowsApp key

    Franjas de una fecha para una modalidad

  • PATCH/meApp key + Customer token

    Editar nombre y teléfono

  • GET/addressesApp key + Customer token

    Libreta de direcciones

  • POST/addressesApp key + Customer token

    Guardar una dirección

  • PATCH/addresses/{id}App key + Customer token

    Editar una dirección

  • GET/ordersApp key + Customer token

    Pedidos del cliente

  • GET/orders/{id}App key + Customer token

    Detalle y seguimiento de un pedido

  • GET/trackingApp key + Customer token

    Pedidos en curso

  • GET/subscriptionsApp key + Customer token

    Planes del cliente

  • GET/subscriptions/{id}App key + Customer token

    Detalle con sus ciclos

  • DELETE/subscriptions/{id}App key + Customer token

    Cancelar el plan

  • POST/subscriptions/{id}/pauseApp key + Customer token

    Pausar

  • POST/subscriptions/{id}/resumeApp key + Customer token

    Reanudar

  • GET/advisory/cases/{id}App key + Customer token

    Case detail

  • GET/advisory/cases/{id}/dossiersApp key + Customer token

    Document folders of the case

  • GET/advisory/cases/{id}/visitsApp key + Customer token

    Visits of the case

  • POST/advisory/visits/{id}/acknowledgeApp key + Customer token

    Acknowledge a published visit report

  • POST/devicesApp key + Customer token

    Registrar el token push del dispositivo

  • DELETE/devices/{id}App key + Customer token

    Revocar el token

Referencia de la API

Boutique storefront API

Products with variants, addresses, delivery quotes (incl. weight-based shipping) and orders of one store.

Products with variants, addresses, delivery quotes (incl. weight-based shipping) and orders of one store.

Base URLhttps://fina.do/api/boutique/v1
AuthenticationX-Boutique-App-Key + customer bearer token on private routes
OpenAPI 3.1/api/boutique/v1/openapi.json
Built forCustom customer apps and websites for a store

Shared rules (app key, envelope, /config, delivery quotes): Storefront APIs.

Endpoints

72 endpointshttps://fina.do/api/boutique/v1
  • GET/locationsApp key

    Sucursales activas donde se puede recoger

  • GET/windowsApp key

    Franjas de una fecha para una modalidad

  • PATCH/meApp key + Customer token

    Editar nombre y teléfono

  • GET/addressesApp key + Customer token

    Libreta de direcciones

  • POST/addressesApp key + Customer token

    Guardar una dirección

  • PATCH/addresses/{id}App key + Customer token

    Editar una dirección

  • GET/ordersApp key + Customer token

    Pedidos del cliente

  • GET/orders/{id}App key + Customer token

    Detalle y seguimiento de un pedido

  • GET/trackingApp key + Customer token

    Pedidos en curso

  • GET/advisory/cases/{id}App key + Customer token

    Case detail

  • GET/advisory/cases/{id}/dossiersApp key + Customer token

    Document folders of the case

  • GET/advisory/cases/{id}/visitsApp key + Customer token

    Visits of the case

  • POST/advisory/visits/{id}/acknowledgeApp key + Customer token

    Acknowledge a published visit report

  • POST/devicesApp key + Customer token

    Registrar el token push del dispositivo

  • DELETE/devices/{id}App key + Customer token

    Revocar el token

Referencia de la API

Restaurant storefront API

Menu, table QR ordering, waiter calls, table reservations, delivery and pickup orders of one restaurant.

Menu, table QR ordering, waiter calls, table reservations, delivery and pickup orders of one restaurant.

Base URLhttps://fina.do/api/restaurant/v1
AuthenticationX-Restaurant-App-Key + customer bearer token on private routes
OpenAPI 3.1/api/restaurant/v1/openapi.json
Built forCustom customer apps and websites for a restaurant

Shared rules (app key, envelope, /config, delivery quotes): Storefront APIs.

Endpoints

90 endpointshttps://fina.do/api/restaurant/v1
  • GET/locationsApp key

    Sucursales activas donde se puede recoger

  • GET/catalogApp key

    La carta: categorías del menú y platos con sus tamaños, extras y alérgenos

  • GET/windowsApp key

    Franjas de una fecha para una modalidad

  • PATCH/meApp key + Customer token

    Editar nombre y teléfono

  • GET/addressesApp key + Customer token

    Libreta de direcciones

  • POST/addressesApp key + Customer token

    Guardar una dirección

  • PATCH/addresses/{id}App key + Customer token

    Editar una dirección

  • GET/ordersApp key + Customer token

    Pedidos del cliente

  • GET/trackingApp key + Customer token

    Pedidos en curso

  • GET/subscriptionsApp key + Customer token

    Planes del cliente

  • GET/subscriptions/{id}App key + Customer token

    Un plan del cliente con sus créditos del ciclo y sus ciclos

  • POST/subscriptions/{id}/pauseApp key + Customer token

    Pausar (los créditos ya dados siguen valiendo hasta que caduquen)

  • GET/advisory/cases/{id}App key + Customer token

    Case detail

  • GET/advisory/cases/{id}/dossiersApp key + Customer token

    Document folders of the case

  • GET/advisory/cases/{id}/visitsApp key + Customer token

    Visits of the case

  • POST/advisory/visits/{id}/acknowledgeApp key + Customer token

    Acknowledge a published visit report

  • POST/devicesApp key + Customer token

    Registrar el token push del dispositivo

  • DELETE/devices/{id}App key + Customer token

    Revocar el token

  • GET/tables/{token}/callsApp key

    Cómo van las llamadas de la mesa (abierta o atendida)

  • GET/reservationsApp key

    Mis reservas (requiere sesión)

  • POST/reservations/{id}/cancelApp key

    Cancelar una reserva propia que aún no empezó

Referencia de la API

Barbershop booking API

Services, staff, live slots, appointments, waitlist and reviews of one barbershop.

Services, staff, live slots, appointments, waitlist and reviews of one barbershop.

Base URLhttps://fina.do/api/barbershop/v1
AuthenticationX-Barbershop-App-Key + customer bearer token on private routes
OpenAPI 3.1/api/barbershop/v1/openapi.json
Built forCustom booking apps for a barbershop or salon

Shared rules (app key, envelope, /config, delivery quotes): Storefront APIs.

Endpoints

58 endpointshttps://fina.do/api/barbershop/v1
  • GET/slotsApp key

    Calculate currently available appointment starts

  • GET/meApp key + Customer token

    Get the linked profile for this business

  • GET/appointmentsApp key + Customer token

    List appointments belonging to the linked customer

  • GET/appointments/{id}App key + Customer token

    Get one appointment belonging to the linked customer

  • GET/appointments/{id}/rescheduleApp key + Customer token

    Get valid replacement starts for an appointment

  • POST/appointments/{id}/reviewApp key + Customer token

    Review a completed appointment

  • GET/waitlistApp key + Customer token

    List the linked customer waitlist entries

  • DELETE/waitlist/{id}App key + Customer token

    Cancel one waiting entry belonging to the linked customer

  • GET/standingApp key + Customer token

    List the customer's standing appointments

  • GET/advisory/cases/{id}App key + Customer token

    Case detail

  • GET/advisory/cases/{id}/dossiersApp key + Customer token

    Document folders of the case

  • GET/advisory/cases/{id}/visitsApp key + Customer token

    Visits of the case

  • POST/advisory/visits/{id}/acknowledgeApp key + Customer token

    Acknowledge a published visit report

  • POST/devicesApp key + Customer token

    Register this installation and its current push token

  • DELETE/devicesApp key + Customer token

    Revoke this app installation for the signed-in account

Referencia de la API

Real estate API

Published properties with filters and visit requests.

Published properties with filters and visit requests.

Base URLhttps://fina.do/api/real-estate/v1
AuthenticationX-Real-Estate-App-Key (+ customer bearer token to list or cancel own visits)
OpenAPI 3.1/api/real-estate/v1/openapi.json
Built forProperty listing apps and websites

Shared rules (app key, envelope, /config, delivery quotes): Storefront APIs.

Endpoints

9 endpointshttps://fina.do/api/real-estate/v1
  • GET/configApp key

    Get app configuration, business identity (business.vr: FinaVr | null, the office tour), filter facets (including virtual_tours: true when any published property has a 360 tour), agents.enabled (true when at least one agent is visible) location.default_center ({ lat, lng } centroid of the published coordinates, or null) and the currencies. currency is the business currency (kept for compatibility); currencies is [{ code, symbol, minor_unit }] with the business currency first and every other currency used by a published property (format each price with ITS currency_code); facets.currencies lists the codes present in published properties; facets.price_range_minor is { rent, sale } normalized to the business currency with the latest rate of each currency (a currency without a rate is taken as is), and facets.price_range_minor_by_currency is the raw { USD: { rent, sale }, DOP: { rent, sale } } per currency (null when no price)

  • GET/catalogApp key

    List published properties with filters (including agent_id and has_virtual_tour=true|1; any other value is INVALID_REQUEST), sorting and cursor pagination. Prices may come in different currencies: min_price_minor/max_price_minor and sort=price_asc|price_desc compare prices NORMALIZED to the business currency using the latest exchange rate of each currency (a currency without a rate is compared as is, unconverted). Each item carries currency_code, rent_price_minor/sale_price_minor in that currency and converted ({ currency_code: business currency, rent_price_minor, sale_price_minor, rate_as_of: YYYY-MM-DD } | null; null when the property is already in the business currency or has no rate). Each item carries has_virtual_tour (boolean), has_vr (boolean, Fina VR tour or AR model) and agent { id, name, photo_url } | null. With lat+lng every item also carries distance_m (integer meters, haversine to the published coordinate: the rounded one when the location is approximate; null without coordinates)

  • GET/properties/{id}App key

    Get a published property with gallery, features, location, nearby_places (kind + distance_m, ordered by distance), virtual_tour ({ url, provider: simple_virtual_tour } | null, url is the 360 viewer to embed in a WebView; kept for compatibility), vr (FinaVr | null: the same tour plus kind/cover/title and the AR model) its agent (full Agent object, or null to fall back to the business contact), currency_code with rent_price_minor/sale_price_minor in that currency, and converted ({ currency_code: business currency, rent_price_minor, sale_price_minor, rate_as_of } | null)

  • GET/agentsApp key

    List the visible agents of the business, ordered by name: { agents: [{ id, name, photo_url, title, languages, properties_count }] }

  • GET/agents/{id}App key

    Get one visible agent (Agent schema). A missing, non-public, inactive or foreign agent answers 404 AGENT_NOT_FOUND

  • GET/visit-requestsCustomer token

    List the signed-in customer visit requests, each with the agent { id, name, photo_url, phone, whatsapp_phone } | null that will contact the customer

  • POST/visit-requestsApp key

    Create a visit request (guest, or signed in when the business requires it)

  • DELETE/meCustomer token

    Delete the signed-in customer account for this business. Returns { deleted, login_removed, pending_visits_kept }

  • POST/visit-requests/{id}/cancelCustomer token

    Cancel a visit request of the signed-in customer

Referencia de la API

Rent a car API

Fleet catalog, availability, customer accounts, reservations with pickup or delivery, and Fina Pay payment.

Fleet catalog, availability, customer accounts, reservations with pickup or delivery, and Fina Pay payment.

Base URLhttps://fina.do/api/rent-car/v1
AuthenticationX-Rent-Car-App-Key (+ customer bearer token for account, reservations, addresses and devices)
OpenAPI 3.1/api/rent-car/v1/openapi.json
Built forCar rental apps and websites

Shared rules (app key, envelope, /config, delivery quotes): Storefront APIs.

Endpoints

66 endpointshttps://fina.do/api/rent-car/v1
  • GET/catalogApp key

    List rentable vehicles and prices, optionally filtered by make/model. Each vehicle carries has_vr (boolean) and vr (FinaVr | null: 360 object / tour and AR model; the catalog is the vehicle detail). /config carries business.vr (the pickup office tour)

  • GET/availabilityApp key

    Get monthly availability for a vehicle

  • GET/reservationsApp key + Customer token

    My reservations (newest first, up to 50)

  • GET/reservations/{id}App key + Customer token

    Reservation detail

  • PATCH/meappKey + Customer token

    Editar nombre y teléfono

  • GET/addressesappKey + Customer token

    Libreta de direcciones

  • POST/addressesappKey + Customer token

    Guardar una dirección

  • PATCH/addresses/{id}appKey + Customer token

    Editar una dirección

  • GET/locationsApp key

    Sucursales activas donde se puede recoger

  • POST/devicesappKey + Customer token

    Registrar el token push del dispositivo

  • DELETE/devices/{id}appKey + Customer token

    Revocar el token

  • GET/advisory/cases/{id}appKey + Customer token

    Case detail

  • GET/advisory/cases/{id}/dossiersappKey + Customer token

    Document folders of the case

  • GET/advisory/cases/{id}/visitsappKey + Customer token

    Visits of the case

  • POST/advisory/visits/{id}/acknowledgeappKey + Customer token

    Acknowledge a published visit report

Referencia de la API

Restaurant staff API

Kitchen queue, floor plan, cashier, deliveries and reservations for paired staff devices.

Kitchen queue, floor plan, cashier, deliveries and reservations for paired staff devices.

Base URLhttps://fina.do/api/staff/v1
AuthenticationX-Staff-Device (paired device) + Authorization: Staff <token> (PIN shift)
OpenAPI 3.1/api/staff/v1/openapi.json
Built forKitchen displays, POS and waiter apps paired by the owner

Two factors: the business owner pairs a device from the panel (POST /devices/pair exchanges the pairing code for the device secret sent in X-Staff-Device), then each employee opens a shift with their PIN (POST /sessions → Authorization: Staff <token>). Every shift route checks a role permission.

Endpoints

154 endpointshttps://fina.do/api/staff/v1
  • POST/devices/pairApp key

    Canjea el código del panel y entrega el secreto del dispositivo

  • POST/devices/current/unpairDevice

    Desvincula la tablet con el PIN de alguien con device.manage: revoca su secreto y cierra sus turnos

  • GET/rosterDevice

    Personal que puede entrar en este dispositivo. Cada persona trae pin_length (4, 5 o 6) para que el teclado pare ahí; null = PIN puesto antes de que guardáramos el largo

  • POST/sessionsDevice

    Abre un turno con el PIN

  • DELETE/sessions/currentDevice + Staff shift

    Cierra el turno

  • GET/meDevice + Staff shift

    Empleado, rol, permisos y vistas. business.business_type (bakery, restaurant…) para el vocabulario de la app

  • GET/push-tokenDevice + Staff shift

    Estado del token de este dispositivo: {registered, employee_id, is_me, platform, updated_at}

  • PUT/push-tokenDevice + Staff shift

    Registra el token FCM de este dispositivo y lo ata a la persona del turno; cuerpo {token, platform: ios|android, locale, app_version?}

  • DELETE/push-tokenDevice

    Apaga los avisos de este dispositivo (no exige turno): {revoked}

  • GET/kitchenDevice + Staff shift

    Cola de cocina. Panadería: cada línea trae message («Mensaje en la torta») y reference_image_url (foto firmada, ≤ 10 min) y el pedido scheduled_for + window_start_min/window_end_min si es un encargo. Cada pedido trae payment (ver OrderPayment)

  • POST/orders/{id}/advanceDevice + Staff shift

    Avanza pendiente → en cocina → listo

  • POST/orders/{id}/serveDevice + Staff shift

    Marca servido un pedido listo

  • POST/orders/{id}/voidDevice + Staff shift

    Anula un pedido sin cobrar, con motivo

  • POST/orders/{id}/chargeDevice + Staff shift

    Cobra el pedido y registra el ingreso; cuerpo opcional {payment_method: cash|card|transfer|other, cash_received_minor}

  • GET/cashierDevice + Staff shift

    Pedidos pendientes de cobro (los de las últimas 36 h y los encargos programados desde ayer). Cada pedido trae payment: si on_credit es true (mayoreo a crédito) NO se cobra; collect_minor es lo que hay que cobrar

  • GET/busyDevice + Staff shift

    «Mucha demanda»: si está activo (a mano o por la cola), minutos extra y umbral automático

  • POST/busyDevice + Staff shift

    Enciende o apaga «Mucha demanda» y los minutos extra; el umbral automático requiere device.manage. Al encenderlo avisa a los pedidos en curso

  • POST/orders/{id}/acceptDevice + Staff shift

    Acepta el pedido de un cliente (cocina o caja). Sin respuesta en 10 min se cancela solo

  • POST/orders/{id}/rejectDevice + Staff shift

    Rechaza el pedido de un cliente con un motivo: se cancela y se devuelve lo pagado con Fina Pay

  • GET/deliveriesDevice + Staff shift

    Pedidos a domicilio en curso: cliente, dirección, cobro y quién lo lleva. Cada pedido trae payment (collect_minor = lo que se cobra al entregar; 0 si on_credit o ya pagado → «A crédito · no cobrar»). summary.to_collect_minor y cash_collected_minor excluyen lo a crédito

  • POST/deliveries/{id}/dispatchDevice + Staff shift

    Salgo con el pedido: pasa a en camino

  • POST/deliveries/{id}/deliverDevice + Staff shift

    Pedido entregado

  • GET/cashier/closeDevice + Staff shift

    Cierre de caja del día (?date=YYYY-MM-DD): totales por método, por cajero, efectivo esperado y anulados

  • GET/floorDevice + Staff shift

    Plano con el estado vivo de cada mesa

  • GET/tables/{id}Device + Staff shift

    Cuenta de la mesa

  • PATCH/tables/{id}Device + Staff shift

    Asigna la mesa a un mesero

  • POST/tables/{id}/ordersDevice + Staff shift

    Toma un pedido para la mesa

  • POST/tables/{id}/closeDevice + Staff shift

    Cobra todo lo pendiente de la mesa y la libera; cuerpo opcional {payment_method, cash_received_minor}

  • GET/menuDevice + Staff shift

    La carta (también con menu.availability). Cada plato trae model_3d (modelo 3D publicado de «Platos en AR») o null

  • POST/menu/{dishId}/availabilityDevice + Staff shift

    Agota o devuelve un plato (86), o una opción o extra suyo con value_id

  • GET/callsDevice + Staff shift

    Llamadas de mesa abiertas y atendidas

  • POST/calls/{id}/ackDevice + Staff shift

    Atiende una llamada

  • POST/calls/{id}/resolveDevice + Staff shift

    Resuelve una llamada

  • GET/reservationsDevice + Staff shift

    Reservas del día (?date=YYYY-MM-DD)

  • POST/reservations/{id}/statusDevice + Staff shift

    Confirma, sienta o cancela una reserva

  • GET/dealer/leadsDevice + Staff shift

    Concesionario, «mis prospectos» (?status=open|all): los asignados al vendedor ligado a este empleado; con dealer.leads.view_all, los de todo el equipo. Sin vendedor ligado devuelve linked:false y lista vacía

  • GET/dealer/test-drivesDevice + Staff shift

    Concesionario, «mis pruebas de manejo» (?date=YYYY-MM-DD&days=1..7): las del vendedor ligado a este empleado; con dealer.test_drives.view_all, las de todo el equipo

  • GET/farm/ordersDevice + Staff shift

    Granja, pedidos (?filter=pending|today|upcoming|active|done) con el contador de cada pestaña: {orders, counts}. Cada pedido trae on_credit y payment (OrderPayment)

  • GET/farm/todayDevice + Staff shift

    Granja, «Hoy»: foto del día en una respuesta (?low_stock_threshold=1..1000, 5 por defecto): {date, generated_at, now_min, currency_code, counts{pending,today,active,delivered_today,cancelled_today}, sold_today, next_window|null, alerts, stock|null (solo con farm.stock.manage), couriers}

  • GET/farm/orders/{id}Device + Staff shift

    Granja, detalle de un pedido: artículos, línea de tiempo (by/actor_name/note), avisos y nota del cliente

  • POST/farm/orders/{id}/approveDevice + Staff shift

    Granja, aprueba un pedido que espera aceptación (409 ORDER_ALREADY_APPROVED / ORDER_ALREADY_CANCELLED)

  • POST/farm/orders/{id}/rejectDevice + Staff shift

    Granja, rechaza un pedido que espera aceptación; cuerpo opcional {reason}. Se devuelve lo pagado

  • POST/farm/orders/{id}/statusDevice + Staff shift

    Granja, mueve el estado {status: preparing|ready_for_pickup|on_the_way|delivered, courier_id?}; solo hacia adelante (409 INVALID_TRANSITION / ORDER_NOT_APPROVED)

  • GET/farm/couriersDevice + Staff shift

    Granja, repartidores activos para «¿Quién lo lleva?»

  • GET/farm/packingDevice + Staff shift

    Granja, lista de empaque del día (?date=YYYY-MM-DD) por franja: productos sumados, pedidos y marcas de empacado

  • POST/farm/packing/marksDevice + Staff shift

    Granja, marca empacada una línea {kind:"line", date, window_key, line_key} o un pedido {kind:"order", order_id}

  • DELETE/farm/packing/marks/{id}Device + Staff shift

    Granja, quita una marca de empacado

  • POST/farm/orders/{id}/cancelDevice + Staff shift

    Granja, cancela un pedido {reason}: avisa al cliente y devuelve el stock

  • POST/farm/orders/{id}/alertsDevice + Staff shift

    Granja, avisa al cliente de un pedido {reason, message?} (push order.alert)

  • POST/farm/orders/{id}/alerts/{alertId}/resolveDevice + Staff shift

    Granja, marca resuelto un aviso de pedido

  • GET/farm/noticeDevice + Staff shift

    Granja, aviso general vigente y cuántos clientes tienen la app: {notice|null, app_customers}

  • PUT/farm/noticeDevice + Staff shift

    Granja, publica o reemplaza el aviso general {level, title, message, until?, send_push}

  • DELETE/farm/noticeDevice + Staff shift

    Granja, quita el aviso general

  • GET/farm/productsDevice + Staff shift

    Granja, productos activos con stock, disponibilidad y variantes

  • PATCH/farm/products/{id}Device + Staff shift

    Granja, agota, repone o ajusta el stock {available?, stock?, variant_id?} (disponible = stock mayor a 0)

  • GET/appointmentsDevice + Staff shift

    Citas, agenda del día (?date=YYYY-MM-DD&professional_id=<uuid>|unassigned): con appointments.view las del profesional ligado a este empleado y las sin asignar; con appointments.view_all (que también abre la agenda), las de todo el equipo. Sin profesional ligado devuelve linked:false y lista vacía

  • GET/appointments/pendingDevice + Staff shift

    Citas, solicitudes por aprobar desde hoy: {count, appointments}

  • GET/appointments/{id}Device + Staff shift

    Citas, detalle con nota del cliente, nota interna, respuestas del formulario, precio y las acciones permitidas (actions). Fuera del alcance responde 404

  • POST/appointments/{id}/approveDevice + Staff shift

    Citas, aprueba una solicitud (avisa al cliente). 409 APPOINTMENT_NOT_PENDING / APPOINTMENT_UNPAID

  • POST/appointments/{id}/rejectDevice + Staff shift

    Citas, rechaza una solicitud {reason 3-200}: se cancela y se avisa al cliente. 409 APPOINTMENT_NOT_PENDING

  • POST/appointments/{id}/completeDevice + Staff shift

    Citas, marca completada una cita confirmada (desde 15 min antes de su inicio; no crea ingreso). 409 APPOINTMENT_NOT_STARTED / APPOINTMENT_STATE_CONFLICT

  • POST/appointments/{id}/no-showDevice + Staff shift

    Citas, marca «no asistió» una cita confirmada ya empezada (estado derivado no_show; no avisa al cliente). 409 APPOINTMENT_NOT_STARTED / APPOINTMENT_STATE_CONFLICT

  • DELETE/appointments/{id}/no-showDevice + Staff shift

    Citas, deshace «no asistió» el mismo día local del inicio o el siguiente. 409 APPOINTMENT_STATE_CONFLICT

  • GET/appointments/{id}/slotsDevice + Staff shift

    Citas, huecos libres para reprogramar (?date=YYYY-MM-DD): {date, duration_min, professional_id, starts, reason: null|closed|service_unavailable}

  • POST/appointments/{id}/rescheduleDevice + Staff shift

    Citas, reprograma a un hueco libre {date, start_min} (conserva duración y profesional; avisa al cliente). 409 SLOT_NOT_AVAILABLE / APPOINTMENT_STATE_CONFLICT

  • POST/appointments/{id}/cancelDevice + Staff shift

    Citas, cancela una cita {reason 3-200}: avisa al cliente y revierte el ingreso registrado si el negocio devuelve en cancelaciones. 409 APPOINTMENT_STATE_CONFLICT

  • PUT/appointments/{id}/staff-noteDevice + Staff shift

    Citas, nota interna del equipo {note: ≤1000 | null} (no la ve el cliente)

  • GET/dealer/todayDevice + Staff shift

    Concesionario, «Hoy» del vendedor: {date, generated_at, linked, leads|null, test_drives|null}; exige algún permiso de ver (leads o pruebas), contadores del alcance, siguiente prueba y hasta 5 prospectos en «focus»

  • GET/dealer/leads/{id}Device + Staff shift

    Concesionario, detalle de un prospecto del alcance: línea de tiempo con quién hizo cada cosa, seguimiento pendiente, etapas permitidas, motivos de pérdida y actions (404 fuera del alcance)

  • POST/dealer/leads/{id}/stageDevice + Staff shift

    Concesionario, mueve la etapa {stage: contacted|test_drive|negotiation|financing|lost, note?, lost_reason?, lost_note?} (nunca won). 409 LEAD_STAGE_CONFLICT; 400 INVALID_REQUEST con details.reason

  • POST/dealer/leads/{id}/activitiesDevice + Staff shift

    Concesionario, registra llamada, WhatsApp, nota, visita o correo {kind, body?, follow_up_at?}: {activity_id, first_contact}

  • POST/dealer/leads/{id}/follow-ups/{activityId}/completeDevice + Staff shift

    Concesionario, cierra un seguimiento pendiente del prospecto: {done}

  • GET/dealer/test-drives/{id}Device + Staff shift

    Concesionario, detalle de una prueba de manejo del alcance con hitos y actions (404 fuera del alcance)

  • POST/dealer/test-drives/{id}/confirmDevice + Staff shift

    Concesionario, confirma una prueba solicitada y avisa al cliente. 409 TEST_DRIVE_STATE_CONFLICT

  • POST/dealer/test-drives/{id}/check-inDevice + Staff shift

    Concesionario, check-in {license_verified: true, odometer_out: 0..3000000}. 400 ODOMETER_INVALID / INVALID_REQUEST (LICENSE_NOT_VERIFIED); 409 TEST_DRIVE_STATE_CONFLICT

  • POST/dealer/test-drives/{id}/closeDevice + Staff shift

    Concesionario, cierra la prueba {odometer_in, outcome: liked|undecided|not_interested, feedback?}. 400 ODOMETER_INVALID (ODOMETER_LOWER); 409 TEST_DRIVE_STATE_CONFLICT

  • POST/dealer/test-drives/{id}/no-showDevice + Staff shift

    Concesionario, marca «no asistió» una prueba cuya hora ya pasó y programa un seguimiento: {status, follow_up_at}. 409 TEST_DRIVE_STATE_CONFLICT

  • POST/dealer/test-drives/{id}/cancelDevice + Staff shift

    Concesionario, cancela la prueba {reason 3-200} y avisa al cliente. 409 TEST_DRIVE_STATE_CONFLICT

  • GET/store/todayDevice + Staff shift

    Tienda, la foto del día: contadores (pending, to_pack, ready, shipped, delivered_today, cancelled_today), vendido hoy, el más antiguo por aprobar y stock bajo (null sin store.stock.manage). ?low_stock_threshold=1..1000

  • GET/store/ordersDevice + Staff shift

    Tienda, pedidos por pestaña (?filter=pending|to_pack|ready|shipped|done) con el contador de cada una

  • GET/store/orders/{id}Device + Staff shift

    Tienda, detalle de un pedido: artículos con foto, talla/color y SKU, historial con quién hizo cada cambio, guía de envío y las acciones permitidas (actions)

  • POST/store/orders/{id}/approveDevice + Staff shift

    Tienda, aprueba un pedido por aprobar (registra el ingreso y descuenta el stock, igual que el panel). 409 ORDER_ALREADY_APPROVED / ORDER_ALREADY_CANCELLED / NO_INCOME_ACCOUNT

  • POST/store/orders/{id}/rejectDevice + Staff shift

    Tienda, rechaza un pedido por aprobar {reason 3-200}: se cancela y se avisa al cliente. 409 ORDER_ALREADY_APPROVED / ORDER_ALREADY_CANCELLED / ORDER_PAID (pagado en línea: se rechaza desde el panel para devolver el dinero)

  • POST/store/orders/{id}/statusDevice + Staff shift

    Tienda, mueve la entrega {status: preparing|ready_for_pickup|on_the_way|delivered, carrier?, tracking_code?} (guía solo al enviar). 409 ORDER_NOT_APPROVED / INVALID_TRANSITION

  • GET/store/productsDevice + Staff shift

    Tienda, productos activos con stock y variantes con sus atributos (?filter=all|low|out&q=&low_stock_threshold=)

  • PATCH/store/products/{id}Device + Staff shift

    Tienda, agota, repone o ajusta el stock {available?, stock?, variant_id?} (disponible = stock mayor a 0)

  • GET/realestate/visitsDevice + Staff shift

    Inmobiliaria, visitas por bandeja (?tab=new|today|upcoming|past; sin tab, «new» si hay nuevas): {tab, scope, is_agent, counts, unassigned_count, visits}. Con realestate.visits.view las del agente (employee) y las sin asignar si es agente público; con realestate.visits.view_all, todas

  • GET/realestate/visits/{id}Device + Staff shift

    Inmobiliaria, detalle de una visita: propiedad, cliente, mensaje, nota interna, sellos, hora sugerida (suggested) y acciones permitidas (actions). Fuera del alcance responde 404

  • POST/realestate/visits/{id}/scheduleDevice + Staff shift

    Inmobiliaria, confirma con hora o cambia la hora {date: YYYY-MM-DD, time_min: 0-1439 múltiplo de 15, no en el pasado, hasta hoy + 90}. 409 VISIT_STATE_CONFLICT

  • POST/realestate/visits/{id}/doneDevice + Staff shift

    Inmobiliaria, marca realizada una visita confirmada (desde 30 min antes) con resultado opcional {outcome: interested|not_interested|wants_another_visit|made_offer|null}. 409 VISIT_NOT_STARTED / VISIT_STATE_CONFLICT

  • POST/realestate/visits/{id}/no-showDevice + Staff shift

    Inmobiliaria, marca «no asistió» una visita confirmada ya empezada (estado derivado no_show; no avisa al cliente). 409 VISIT_NOT_STARTED / VISIT_STATE_CONFLICT

  • DELETE/realestate/visits/{id}/no-showDevice + Staff shift

    Inmobiliaria, deshace «no asistió» el mismo día local de la visita o el siguiente. 409 VISIT_STATE_CONFLICT

  • POST/realestate/visits/{id}/cancelDevice + Staff shift

    Inmobiliaria, cancela una visita pendiente o confirmada {reason 3-200}. 409 VISIT_STATE_CONFLICT

  • PUT/realestate/visits/{id}/noteDevice + Staff shift

    Inmobiliaria, nota interna del equipo {note: ≤1000 | null} (no la ve el cliente)

  • POST/realestate/visits/{id}/contactDevice + Staff shift

    Inmobiliaria, registra un contacto con el cliente {channel: call|whatsapp|email}: {contacted_at, last_contact_at, contact_count}. 409 VISIT_STATE_CONFLICT

  • POST/realestate/visits/{id}/claimDevice + Staff shift

    Inmobiliaria, un agente público toma una visita sin asignar. 409 VISIT_ALREADY_ASSIGNED / AGENT_NOT_AVAILABLE

  • POST/realestate/visits/{id}/assignDevice + Staff shift

    Inmobiliaria, asigna o quita el agente {agent_id: uuid | null}; exige además realestate.visits.view_all (403 FORBIDDEN). Avisa al agente nuevo. 409 AGENT_NOT_AVAILABLE / VISIT_STATE_CONFLICT

  • GET/realestate/agentsDevice + Staff shift

    Inmobiliaria, agentes públicos activos para «Asignar agente»: {agents: [{id, name, photo_url, open_visits, is_me}]}

  • GET/realestate/propertiesDevice + Staff shift

    Inmobiliaria, propiedades en alcance (?status=available|reserved|all, «available» por defecto): {scope, counts{available,reserved,other}, properties} con visitas pendientes y próximas

  • PATCH/realestate/properties/{id}Device + Staff shift

    Inmobiliaria, cambia una propiedad entre disponible y reservada {status: available|reserved}. 409 PROPERTY_STATE_CONFLICT (desde alquilada, vendida o inactiva)

  • GET/rentcar/todayDevice + Staff shift

    Rent Car, «Hoy»: {date, generated_at, counts{pending,pickups,returns,not_returned,active}, fleet{available,rented,in_shop,inactive}, pickups, returns}. 404 NOT_FOUND si el negocio no tiene el módulo

  • GET/rentcar/reservationsDevice + Staff shift

    Rent Car, reservas de vehículos (?tab=pending|pickups|returns|active|upcoming; sin tab, la primera con reservas): {tab, counts, reservations}

  • GET/rentcar/reservations/{id}Device + Staff shift

    Rent Car, detalle: reserva, nota, servicios, inspecciones (URLs firmadas, inspected_by_name), comparison salida/llegada, pickup_reference y las acciones permitidas (actions)

  • POST/rentcar/reservations/{id}/approveDevice + Staff shift

    Rent Car, aprueba una reserva pendiente (avisa al cliente). 409 RESERVATION_STATE_CONFLICT / RESERVATION_AWAITING_PAYMENT (se confirma sola al pagar)

  • POST/rentcar/reservations/{id}/rejectDevice + Staff shift

    Rent Car, rechaza una reserva pendiente {reason 3-200}: se cancela, se cancela el cobro pendiente y se avisa al cliente. 409 RESERVATION_STATE_CONFLICT

  • POST/rentcar/reservations/{id}/photosDevice + Staff shift

    Rent Car, sube una foto de inspección (multipart, campo file; 10 MB): {id, url}. 413 FILE_TOO_LARGE / 415 UNSUPPORTED_IMAGE / 429 UPLOAD_LIMIT / 503 STORAGE_UNAVAILABLE

  • DELETE/rentcar/reservations/{id}/photos/{photoId}Device + Staff shift

    Rent Car, borra una foto que aún no está en una inspección guardada

  • POST/rentcar/reservations/{id}/checkoutDevice + Staff shift

    Rent Car, check-out {odometer_km, fuel_eighths 0-8, notes?, photos[1-24]{id, angle, damage_note?}}: guarda la inspección de salida con el nombre del empleado y pasa a en curso. 409 RESERVATION_NOT_STARTED / VEHICLE_IN_SHOP / RESERVATION_STATE_CONFLICT; 400 INSPECTION_NEEDS_PHOTOS / INVALID_PHOTO

  • POST/rentcar/reservations/{id}/checkinDevice + Staff shift

    Rent Car, check-in (mismo cuerpo): guarda la inspección de llegada, completa la reserva y devuelve {status, inspection, comparison}. También recibe una completada por el cron (hasta 3 días). 409 INSPECTION_REQUIRED; 400 ODOMETER_BELOW_PICKUP {pickup_km} / INSPECTION_NEEDS_PHOTOS / INVALID_PHOTO

  • GET/rentcar/fleetDevice + Staff shift

    Rent Car, flota (?state=all|available|rented|in_shop): {counts, vehicles} con estado derivado, alquiler en curso, próxima reserva y reservas por venir

  • POST/rentcar/fleet/{id}/shopDevice + Staff shift

    Rent Car, manda un vehículo al taller {note?≤200}: deja de verse en la app de clientes; las reservas no cambian. {state, upcoming_count}. 409 VEHICLE_RENTED / VEHICLE_IN_SHOP

  • DELETE/rentcar/fleet/{id}/shopDevice + Staff shift

    Rent Car, saca un vehículo del taller: {state: available|rented}. 409 VEHICLE_NOT_IN_SHOP

  • GET/loyaltyDevice + Staff shift

    Fidelidad, el programa resumido: {program: {name, status, points_label_singular, points_label_plural, currency_code, enrollment_mode, starts_at, ends_at, rules{per_purchase, per_visit, per_amount, min_purchase_minor}, self_check_in}|null}

  • GET/loyalty/membersDevice + Staff shift

    Fidelidad, busca socios (?q= nombre o teléfono&cursor=): {members, next_cursor}. Cada socio trae nombre, teléfono enmascarado («•••• 1234»; nunca el correo), saldo, estado y última actividad

  • POST/loyalty/membersDevice + Staff shift

    Fidelidad, inscribe un cliente {client_id} o crea el cliente {name, phone} y lo inscribe: {created, client_created, member}. 403 LOYALTY_MEMBER_INACTIVE si está bloqueado

  • GET/loyalty/members/by-card/{token}Device + Staff shift

    Fidelidad, el socio de una tarjeta escaneada; {token} es el token o la URL completa del QR (https://…/f/<token>, codificada). 404 LOYALTY_NOT_FOUND

  • GET/loyalty/members/{id}Device + Staff shift

    Fidelidad, detalle del socio: saldo, canjes pendientes (pending_redemptions) y últimos 20 movimientos (movements)

  • POST/loyalty/members/{id}/earnDevice + Staff shift

    Fidelidad, acredita {kind: visit|purchase|visit_purchase, amount_minor?, store_order_id?, invoice_id?}. Idempotency-Key OBLIGATORIO (400 IDEMPOTENCY_KEY_REQUIRED). Con store_order_id (aprobado) o invoice_id (pagada) el origen es ese documento, del MISMO negocio y del cliente del socio (el pedido de sala sin cliente se permite asociar), y el IMPORTE lo toma el servidor (amount_minor se ignora); no da puntos dos veces. Sin documento, staff_purchase con amount_minor acotado a 5000000 (400 INVALID_REQUEST si lo supera) y siempre sujeto al tope diario de puntos. {status: awarded|candidate|duplicate, points, balance}. 409 LOYALTY_PROGRAM_INACTIVE / ORDER_STATE_CONFLICT / ORDER_CLIENT_MISMATCH / INVOICE_STATE_CONFLICT / INVOICE_CLIENT_MISMATCH

  • POST/loyalty/members/{id}/adjustDevice + Staff shift

    Fidelidad, ajusta puntos a mano {delta ≠ 0, reason 3-200}. Idempotency-Key OBLIGATORIO. 409 LOYALTY_INSUFFICIENT_POINTS si dejaría el saldo bajo 0

  • GET/loyalty/redemptionsDevice + Staff shift

    Fidelidad, canjes del negocio (?status=pending|used|expired|cancelled, pending por defecto), los 50 más recientes

  • POST/loyalty/redemptions/validateDevice + Staff shift

    Fidelidad, mira un código de canje SIN marcarlo {code}: {valid, error_code, redemption|null}

  • POST/loyalty/redemptions/{id}/useDevice + Staff shift

    Fidelidad, entrega la recompensa (marca usado). 409 LOYALTY_CODE_ALREADY_USED / LOYALTY_CODE_CANCELLED, 410 LOYALTY_CODE_EXPIRED

  • POST/loyalty/redemptions/{id}/cancelDevice + Staff shift

    Fidelidad, cancela un canje y devuelve los puntos {reason 3-200?}. Un canje ya usado solo lo cancela quien tiene loyalty.adjust y con motivo (403 LOYALTY_NOT_AVAILABLE si no; el permiso base se acepta también con solo loyalty.adjust)

  • GET/advisory/casesDevice + Staff shift

    Asesoría, mis casos (?status=open|all|requested|active|on_hold|completed|cancelled|declined, open por defecto &stage=<key>&q=&mine=1): {cases}. Sin advisory.view_all solo los que lidero o donde tengo una visita asignada; con view_all, los de todo el equipo (mine=1 los acota a los míos). También lo abre view_all

  • POST/advisory/casesDevice + Staff shift

    Asesoría, crea un caso activo {client_id | client_name+client_phone, case_type_id, title, summary?, owner_employee_id?, priority?, stage_key?, target_date?, site_name?, site_address?, site_lat?, site_lng?}: {case, client_created}. Idempotency-Key OBLIGATORIO (reenviar devuelve el mismo caso)

  • GET/advisory/case-typesDevice + Staff shift

    Asesoría, tipos de caso vigentes con sus etapas, para crear un caso: {case_types}

  • GET/advisory/employeesDevice + Staff shift

    Asesoría, el equipo para «Asignar a…» (responsable o técnico de una visita): {employees}

  • GET/advisory/cases/{id}Device + Staff shift

    Asesoría, detalle del caso: {case, activities (las 50 últimas, todas las visibilidades)}. El teléfono del cliente va enmascarado («•••• 1234») y sin correo salvo advisory.view_all; los contadores de mercado solo cuentan con advisory.market. También lo abre view_all o advisory.visits. 404 ADVISORY_NOT_FOUND si no es de mi alcance

  • GET/advisory/cases/{id}/contactDevice + Staff shift

    Asesoría, el teléfono real para llamar o escribir por WhatsApp {name, phone, tel, whatsapp}. Registra el acceso en la actividad del staff. También lo abre view_all o advisory.visits

  • POST/advisory/cases/{id}/activitiesDevice + Staff shift

    Asesoría, anota en la bitácora {kind: note|call|whatsapp|email|meeting|task, body ≤4000, visibility: internal|client, due_at? (obligatorio en task)}: {activity}. Idempotency-Key OBLIGATORIO. 409 ADVISORY_CASE_CLOSED si el caso está cerrado y no es una nota interna

  • POST/advisory/cases/{id}/activities/{aid}/doneDevice + Staff shift

    Asesoría, marca hecha una tarea (idempotente): {activity}

  • POST/advisory/cases/{id}/stageDevice + Staff shift

    Asesoría, cambia de etapa {stage_key, tell_client?}: {case, stage_key}. 409 ADVISORY_INVALID_TRANSITION si el caso no está abierto o ya está en esa etapa

  • POST/advisory/cases/{id}/statusDevice + Staff shift

    Asesoría, cambia el estado {action: accept|decline|hold|resume|complete|cancel|reopen, outcome? (complete), reason? (decline|cancel), owner_employee_id?, stage_key? (accept)}: {case}. 409 ADVISORY_INVALID_TRANSITION / 422 ADVISORY_FIELDS_MISSING

  • POST/advisory/cases/{id}/ownerDevice + Staff shift

    Asesoría, cambia o quita el responsable {employee_id: uuid | null}: {case}. Exige además advisory.view_all (403 ADVISORY_FORBIDDEN)

  • GET/advisory/cases/{id}/dossiersDevice + Staff shift

    Asesoría, expedientes del caso con ítems (estado, motivo de rechazo, vencimiento) y archivos (sin rutas de almacenamiento): {dossiers}. También lo abre view_all

  • POST/advisory/dossier-items/{id}/reviewDevice + Staff shift

    Asesoría, revisa un documento {action: approve|reject|waive|unwaive|reopen, reason? (obligatorio en reject|waive|reopen), expires_on? (obligatorio al aprobar un ítem que vence)}: {item}. 409 ADVISORY_ITEM_LOCKED / ADVISORY_INVALID_TRANSITION

  • POST/advisory/dossier-items/{id}/filesDevice + Staff shift

    Asesoría, adjunta una subida a un documento {upload_id} (de POST /advisory/uploads): {item}. 415 ADVISORY_FILE_TYPE si el ítem no acepta ese tipo

  • GET/advisory/files/{id}/urlDevice + Staff shift

    Asesoría, URL firmada y corta de un archivo del expediente {url, expires_in}; el acceso se registra antes de firmar. Las fotos de visita también las abre advisory.visits. 404 ADVISORY_NOT_FOUND si no es de mi alcance

  • POST/advisory/uploadsDevice + Staff shift

    Asesoría, sube un archivo (bytes en el cuerpo con Content-Type y X-File-Name; ?purpose=dossier|visit_photo, dossier por defecto: visit_photo exige advisory.visits): 201 {upload_id, mime, size_bytes}. Mismo validador que el cliente: 413 ADVISORY_FILE_TOO_LARGE (15 MB), 415 ADVISORY_FILE_TYPE (jpeg/png/webp/heic/pdf, por contenido real), 429 ADVISORY_UPLOAD_LIMIT

  • GET/advisory/visitsDevice + Staff shift

    Asesoría, la agenda de visitas (?from=YYYY-MM-DD&to=YYYY-MM-DD en hora RD, to inclusive; sin rango trae también las por agendar; &mine=1): {visits}. Sin advisory.view_all solo las mías. También lo abre view_all

  • GET/advisory/visits/{id}Device + Staff shift

    Asesoría, la visita para su informe: {visit, case, employee, metrics (indicadores medibles con rango y último valor), photos}. El teléfono va enmascarado; el real por /cases/{id}/contact. También lo abren view_all y view

  • POST/advisory/cases/{id}/visitsDevice + Staff shift

    Asesoría, crea una visita {scheduled_at?, duration_min?, employee_id?, site_address?, purpose?, request_note?}: con fecha nace agendada, sin ella por agendar; sin employee_id la toma quien la crea. Asignar a otra persona exige advisory.assign. Idempotency-Key OBLIGATORIO. También lo abre advisory.manage

  • POST/advisory/visits/{id}/scheduleDevice + Staff shift

    Asesoría, agenda una visita pedida o reprograma una agendada {scheduled_at, duration_min?, employee_id?, site_address?, purpose?}. También lo abre advisory.manage

  • POST/advisory/visits/{id}/startDevice + Staff shift

    Asesoría, inicia la visita (agendada → en curso). 403 ADVISORY_FORBIDDEN si no es mía y no tengo view_all

  • POST/advisory/visits/{id}/cancelDevice + Staff shift

    Asesoría, cancela la visita {reason?}. También lo abre advisory.manage

  • POST/advisory/visits/{id}/no-showDevice + Staff shift

    Asesoría, marca «no estaba» una visita agendada

  • PUT/advisory/visits/{id}/reportDevice + Staff shift

    Asesoría, guarda el borrador del informe {findings ≤8000, recommendations[{id, text, priority: high|medium|low, due_on?, status: open|done|dropped}], measurements[{case_metric_id, value, note?}], photos[{upload_id, caption?}] (de POST /advisory/uploads?purpose=visit_photo, hasta 40)}: {visit}. Se puede repetir mientras la visita no esté cancelada

  • POST/advisory/visits/{id}/completeDevice + Staff shift

    Asesoría, completa la visita {report?}; exige hallazgos o al menos una recomendación (422 ADVISORY_FIELDS_MISSING). Las mediciones pasan a ser registros del indicador

  • POST/advisory/visits/{id}/publishDevice + Staff shift

    Asesoría, publica el informe al cliente: {visit, version, republished, pdf, report_url}. Republicar sube la versión con el mismo enlace

  • GET/advisory/cases/{id}/metricsDevice + Staff shift

    Asesoría, indicadores del caso con su serie e historial: {metrics}. También lo abren view_all y advisory.visits

  • POST/advisory/cases/{id}/metrics/{cmId}/recordsDevice + Staff shift

    Asesoría, registra una medición {value, measured_on, note?} dentro del rango del indicador: 201 {record_id}. Idempotency-Key OBLIGATORIO. También lo abre advisory.visits. 404 si el indicador no es de ese caso

  • GET/advisory/buyersDevice + Staff shift

    Asesoría, compradores activos (?q=): {buyers}. Teléfono enmascarado y sin correo salvo advisory.view_all

  • GET/advisory/cases/{id}/opportunitiesDevice + Staff shift

    Asesoría, oportunidades del caso con su comprador y oferta: {opportunities}

  • POST/advisory/opportunities/{id}/statusDevice + Staff shift

    Asesoría, mueve una oportunidad {status: draft|shared|negotiating|agreed|won|lost|withdrawn, price_minor?, currency_code?, volume?, closed_reason?, next_step?}: {opportunity}. Ganar exige precio y volumen (422 ADVISORY_FIELDS_MISSING)

Referencia de la API

Video Studio API

Quoted, idempotent AI media jobs paid with your Fina credits.

Quoted, idempotent AI media jobs paid with your Fina credits.

Base URLhttps://fina.do/api/video-studio/v1
AuthenticationBearer personal API key (fina_vs_…)
OpenAPI 3.1/api/video-studio/v1/openapi.json
Built forScripts and tools of a Fina Video Studio user

Create a personal API key in your Fina profile. Every spending mutation needs an Idempotency-Key header; credits are reserved at the quoted maximum and never charged above it.

Endpoints

14 endpointshttps://fina.do/api/video-studio/v1
  • GET/mePersonal key

    Identity, plan and spendable credits

  • GET/businessesPersonal key

    Your businesses (not archived), by name

  • GET/localsPersonal key

    Your locals («Mis locales»: visual profiles of your physical spaces)

  • GET/locals/{id}Personal key

    One of your locals: description, usable photos and the linked business

  • GET/catalogPersonal key

    Server-driven catalog of operations and models

  • POST/quotesPersonal key

    Create an immutable, expiring quote

  • POST/uploadsPersonal key

    Register an input and get a signed upload URL

  • POST/uploads/{id}/completePersonal key

    Verify the uploaded object (existence, size, sha256 when feasible)

  • GET/jobsPersonal key

    List jobs (cursor pagination)

  • POST/jobsPersonal key

    Create a job (reserves the quoted maximum before any provider call)

  • GET/jobs/{id}Personal key

    Job detail (reconciles with the provider when pending)

  • POST/jobs/{id}/cancelPersonal key

    Cancel a job (free before submit; honest otherwise)

Más

Errors

HTTP status codes and stable error codes of the Fina Pay and storefront APIs.

Fina Pay answers errors as { "error": "<code>" }. Storefront APIs use the envelope { "data": null, "error": { "code", "message", "details"? }, "request_id" }. In both cases branch on the code.

HTTPFina Pay codeWhat to do
400invalid_json, invalid_amount, missing_paramsFix the request. amount_minor must be an integer > 0.
401unauthorizedCheck the secret key and that the connection is active.
400reference_required, invalid_reference, same_account, …Payout request errors, see Payouts.
403payouts_disabledTurn on Payouts via API for the connection.
404not_foundThe charge or payout does not exist or belongs to another connection.
404recipient_not_foundNo active Fina Pay wallet with that account number or phone.
409insufficient_funds, currency_mismatch, wallet_blockedThe payout was not sent; nothing moved.
409not_cancellableThe charge is no longer pending.
429rate_limitedBack off and retry (60 requests/minute per connection).
5xx—Retry with exponential backoff; reuse the same reference.

Each storefront spec lists its codes per status in its OpenAPI (responses). Common ones: UNAUTHENTICATED, APP_NOT_FOUND, BUSINESS_INACTIVE, OUT_OF_DELIVERY_ZONE, SHIPMENT_LIMIT_EXCEEDED, INSUFFICIENT_STOCK, RATE_LIMITED.

Más

Testing and going live

How to test without a sandbox, and a go-live checklist.

There is no separate sandbox environment yet: keys are live and charges move real money. Test with small amounts, then cancel or let them expire.
  1. Create a dedicated connection for testing (for example "Staging") with its own keys and webhook URL, so test charges are booked and reported apart.
  2. Create a charge of 100 (RD$1.00), pay it with a second Fina account and check charge.paid arrives and verifies.
  3. Expose your local server with a tunnel (ngrok, Cloudflare Tunnel) to receive webhooks during development.
  4. Cancel pending test charges with POST /charges/{id}/cancel.

Go-live checklist

  • Secret key and IPN secret live only in server environment variables.
  • Webhook handler verifies x-fina-sig, answers 2xx within 15 s and is idempotent.
  • Charges use your order id as reference; retries reuse it.
  • Money is handled as integers in centavos end to end.
  • You reconcile with GET /charges/{id} when a webhook is missing.

Más

Changelog

Notable changes to the public APIs and these docs.

DateChange
2026-10-08Fina Pay payouts: POST /payouts, GET /payouts/{id} and GET /recipients (off by default, enabled per connection).
2026-10-05New developer portal: OpenAPI spec for Fina Pay (/api/wallet/v1/openapi.json), llms.txt, llms-full.txt, Markdown pages and the read-only docs MCP server.
2026-10-05Storefront APIs: weight-based shipping in /config and /delivery/estimate (items, weight, fee_by_weight, SHIPMENT_LIMIT_EXCEEDED).
2026-10-05Fina Pay subscriptions: hosted checkout /pay/s/<token>, customer portal and subscription.* webhooks (rolling out).
2026-10-04charge.refunded webhook when a paid charge is returned to the customer.
2026-10-03Fina Pay: fee_minor and net_minor on paid charges and in charge.paid.
2026-07-05Fina Pay API v1: charges, payment page and signed IPN webhooks.