Finero Scheduler API

The Scheduler API from Finero — 2 operation(s) for scheduler.

GitHub OpenAPI

Operations 3

GET /v1/scheduled-emails List scheduled payment-link emails #
PATCH /v1/scheduled-emails/{scheduledEmailId} Reschedule a payment-link email #
DELETE /v1/scheduled-emails/{scheduledEmailId} Delete a scheduled payment-link email #

Specifications

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/getfinero-com:getfinero-com-scheduler-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

getfinero-com-scheduler-api-openapi.yml Raw ↑
openapi: 3.2.0
info:
  title: Finero Public Scheduler API
  version: 1.0.0
  description: 'Tenant-scoped public API for Finero — AR / cash-flow automation. Authenticate every request with `Authorization: Bearer ` (keys are created in Settings → API and are tenant-owned). All data belongs to the key''s tenant; no request input can address another tenant. Two permissions exist: **admin** keys can call every operation; **readonly** keys only operations without side effects (`x-permission: readonly`). Lists are cursor-paginated (limit 1–100, follow pagination.next_cursor). Request bodies are capped at 64 KiB. Rate limits: 120/min per key and 600/min per tenant, in fixed windows of 60 s and 60 s. The per-tenant budget is ONE budget per workspace, shared by all its API keys AND its MCP agent connections (each agent connection has its own per-credential limit, the key''s). Heavy operations (`x-heavy: true`: summarizeAging, listCustomerAging, listAgingInstallments, summarizeInvoices) read the whole book on every call and also count against 10 per 10 seconds per tenant (60 a minute, in a short window so they cannot arrive in a large burst). EVERY response that reached the limiter carries `RateLimit-Limit`, `RateLimit-Remaining`, `RateLimit-Reset` (seconds until the window resets) and `RateLimit-Policy` (every quota that applies to the operation — the heavy one too on a heavy operation; static per operation), plus the same figures as the newer combined `RateLimit` field. Pace against them rather than waiting to be refused. The triple describes whichever quota is CLOSEST TO REFUSING (on a 429, the one that refused), so a key that is fine may still show little headroom when the tenant is busy. The counters are shared server-side, not per-instance, so the numbers are exact rather than approximate. Requests rejected BEFORE the limiter — unknown path, wrong method, oversized body, missing/invalid/revoked credentials, a workspace without API access — consume no quota and carry no rate-limit fields. 429 adds `Retry-After` in seconds (RFC 9110); waiting that long is enough, and retrying sooner is refused again. Missing and invalid credentials are limited separately per source IP; that quota is deliberately not published. The windows are FIXED rather than sliding, so a burst spanning a boundary can pass up to twice the limit before being refused — do not treat the limit as a hard ceiling over less than two windows. `RateLimit-Remaining` RISES when a window rolls, for the same reason; an increase is the quota refilling, not an error. Watch `RateLimit-Reset` jump upward to tell a roll from a miscount. A 503 is not the limiter: under extreme concurrency the platform sheds load before the request reaches Finero, so it carries neither rate-limit fields nor Retry-After. Back off and retry. Decimal money amounts are exact strings (never floats); *_minor amounts are integers in the currency''s minor unit. Timestamps are ISO 8601 UTC. **There is a second way in.** This API is for systems and unattended automation, authenticated by a tenant API key that acts as the WORKSPACE. If instead a person is present and connecting an AI assistant to their own workspace, use the Finero MCP server at `https://app.getfinero.com/mcp` — Model Context Protocol over the Streamable HTTP transport (JSON-RPC 2.0, `MCP-Protocol-Version` 2026-07-28, 2025-11-25), authorized with OAuth 2.1 so the session acts as THAT PERSON. Its `server.json` manifest — the MCP registry''s published format — is at `https://app.getfinero.com/mcp/server.json`. It is read-mostly — 24 tools (list_tasks, dismiss_task, summarize_aging, list_customer_aging, list_aging_installments, list_invoices, summarize_invoices, get_invoice, list_payment_integrations, list_payments, list_workflows, list_workflow_executions, list_erp_connections, list_sync_runs, get_sync_run, start_erp_sync, list_payment_links, get_payment_link, create_payment_links, list_scheduled_emails, update_scheduled_email, delete_scheduled_email, push_payment_receipts, deactivate_payment_link).'
servers:
- url: https://api.getfinero.com/functions/v1/api
security:
- apiKey: []
tags:
- name: Scheduler
paths:
  /v1/scheduled-emails:
    get:
      operationId: listScheduledEmails
      summary: List scheduled payment-link emails
      description: 'Lists active schedules by send time; completed/cancelled items are removed. Emails use workspace reply-to/format at send time. Cursor-paginated: pass `limit` (1–100) and send pagination.next_cursor back as `cursor` until has_more is false. To learn HOW MANY rows match without walking every page, pass `include_total=true` once and read pagination.total_count.


        Available to **admin and readonly** API keys (no side effects).'
      tags:
      - Scheduler
      security:
      - apiKey: []
      x-permission: readonly
      x-side-effects: false
      parameters:
      - name: limit
        in: query
        required: false
        description: Page size, 1–100. Default 25.
        schema:
          type: integer
          minimum: 1
          maximum: 100
      - name: cursor
        in: query
        required: false
        description: Opaque pagination cursor from a previous response's pagination.next_cursor.
        schema:
          type: string
          minLength: 1
          maxLength: 400
      - name: order
        in: query
        required: false
        description: 'Sort direction. THIS list is ordered by `scheduled_for` — when the email is due to send — not by creation time, because it is a send queue. Unlike every other list here, that value is MUTABLE: rescheduling through `PATCH /v1/scheduled-emails/{scheduledEmailId}` moves a row within the order. A row rescheduled across your cursor between pages can therefore be missed or returned twice. If you need an exact set, re-walk from the first page, or filter by `status` and reconcile on `id`. Default desc (latest send time first).'
        schema:
          type: string
          enum:
          - asc
          - desc
      - name: include_total
        in: query
        required: false
        description: 'Return pagination.total_count — the number of rows matching the filters, ignoring paging. It is the SAME number on every page, including alongside a cursor. Off by default: an exact count scans the whole filtered set, so asking on each page pays repeatedly for an answer that does not change — ask on the first request and keep it.'
        schema:
          type: boolean
      - name: status
        in: query
        required: false
        description: Filter by scheduler state.
        schema:
          type: string
          enum:
          - scheduled
          - processing
          - failed
      - name: payment_link_id
        in: query
        required: false
        description: Filter by payment link.
        schema:
          type: string
          format: uuid
      responses:
        '200':
          description: Success
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    description: Active scheduled emails.
                    items:
                      type: object
                      properties:
                        id:
                          type: string
                          description: Scheduled email id.
                          format: uuid
                        payment_link_id:
                          type: string
                          description: Payment link this email will deliver.
                          format: uuid
                        scheduled_for:
                          type: string
                          description: Resolved send instant (ISO 8601 UTC).
                          format: date-time
                        timezone:
                          type: string
                          description: Tenant timezone snapshot used for scheduling.
                        status:
                          type: string
                          description: Current scheduler state. Successful deliveries leave this active queue.
                          enum:
                          - scheduled
                          - processing
                          - failed
                        attempt_count:
                          type: integer
                          description: Dispatch attempts so far.
                          minimum: 0
                        next_attempt_at:
                          type:
                          - string
                          - 'null'
                          description: Next bounded retry instant (ISO 8601 UTC).
                          format: date-time
                        last_error_code:
                          type:
                          - string
                          - 'null'
                          description: Categorical failure code; never a provider response or recipient detail.
                        created_at:
                          type: string
                          description: Creation time (ISO 8601 UTC).
                          format: date-time
                        updated_at:
                          type: string
                          description: Last update time (ISO 8601 UTC).
                          format: date-time
                      required:
                      - id
                      - payment_link_id
                      - scheduled_for
                      - timezone
                      - status
                      - attempt_count
                      - next_attempt_at
                      - last_error_code
                      - created_at
                      - updated_at
                      additionalProperties: false
                  pagination:
                    type: object
                    properties:
                      next_cursor:
                        type:
                        - string
                        - 'null'
                        description: Opaque cursor for the next page — pass as ?cursor=. Null when there are no further results.
                      has_more:
                        type: boolean
                        description: Whether another page exists.
                      limit:
                        type: integer
                        description: The page size that was applied.
                      total_count:
                        type: integer
                        description: Total rows matching the filters, ignoring paging. Present ONLY when the request passed ?include_total=true.
                    required:
                    - next_cursor
                    - has_more
                    - limit
                    additionalProperties: false
                required:
                - data
                - pagination
                additionalProperties: false
              example:
                data:
                - id: 22222222-2222-4222-8222-222222222222
                  payment_link_id: 0d9c8b7a-6e5f-4d3c-b2a1-908f7e6d5c4b
                  scheduled_for: '2026-08-14T06:00:00+00:00'
                  timezone: Asia/Jerusalem
                  status: scheduled
                  attempt_count: 0
                  next_attempt_at: null
                  last_error_code: null
                  created_at: '2026-08-10T09:00:00+00:00'
                  updated_at: '2026-08-10T09:00:00+00:00'
                pagination:
                  next_cursor: null
                  has_more: false
                  limit: 25
        '400':
          description: '`invalid_cursor` — The cursor is malformed, was issued for a different query, or (aging) the report changed. Restart without cursor, keeping the filters identical. `validation_failed` — The request failed schema validation. See error.details for field messages.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '401':
          description: '`invalid_credentials` — The credential is malformed or does not match an active key. The response never reveals whether a key identifier exists. `missing_credentials` — No Authorization: Bearer header was sent. `revoked_credentials` — The presented key was revoked. Create a new key in Settings → API.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '403':
          description: '`feature_unavailable` — The tenant''s tier does not currently include API access. Feature rollout flags cannot grant this entitlement. `permission_denied` — The key''s permission does not allow this operation (readonly keys cannot call admin-only operations).'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '404':
          description: '`not_found` — No such resource in YOUR tenant. Ids belonging to another tenant are indistinguishable from missing ones.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '429':
          description: '`rate_limit_exceeded` — Too many requests — for this key, for the workspace (one budget shared by all its API keys and MCP agent connections), or, on a heavy operation, for the workspace''s heavy budget. Honor the Retry-After header (seconds) before retrying — waiting that long is enough. The RateLimit-* fields on every response say how much headroom is left before this happens; pace against them instead of retrying into a refusal.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '500':
          description: '`internal_error` — Unexpected server error. Safe to retry with the same Idempotency-Key.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
  /v1/scheduled-emails/{scheduledEmailId}:
    patch:
      operationId: updateScheduledEmail
      summary: Reschedule a payment-link email
      description: 'Changes the tenant-local send time of an email whose status is `scheduled` or `failed`. A `processing` email cannot be edited while its provider outcome may be in flight.


        Requires an **admin** API key (has side effects).'
      tags:
      - Scheduler
      security:
      - apiKey: []
      x-permission: admin
      x-side-effects: true
      parameters:
      - name: scheduledEmailId
        in: path
        required: true
        description: Scheduled email id.
        schema:
          type: string
          format: uuid
      responses:
        '200':
          description: Success
          content:
            application/json:
              schema:
                type: object
                properties:
                  id:
                    type: string
                    description: Scheduled email id.
                    format: uuid
                  payment_link_id:
                    type: string
                    description: Payment link this email will deliver.
                    format: uuid
                  scheduled_for:
                    type: string
                    description: Resolved send instant (ISO 8601 UTC).
                    format: date-time
                  timezone:
                    type: string
                    description: Tenant timezone snapshot used for scheduling.
                  status:
                    type: string
                    description: Current scheduler state. Successful deliveries leave this active queue.
                    enum:
                    - scheduled
                    - processing
                    - failed
                  attempt_count:
                    type: integer
                    description: Dispatch attempts so far.
                    minimum: 0
                  next_attempt_at:
                    type:
                    - string
                    - 'null'
                    description: Next bounded retry instant (ISO 8601 UTC).
                    format: date-time
                  last_error_code:
                    type:
                    - string
                    - 'null'
                    description: Categorical failure code; never a provider response or recipient detail.
                  created_at:
                    type: string
                    description: Creation time (ISO 8601 UTC).
                    format: date-time
                  updated_at:
                    type: string
                    description: Last update time (ISO 8601 UTC).
                    format: date-time
                required:
                - id
                - payment_link_id
                - scheduled_for
                - timezone
                - status
                - attempt_count
                - next_attempt_at
                - last_error_code
                - created_at
                - updated_at
                additionalProperties: false
              example:
                id: 22222222-2222-4222-8222-222222222222
                payment_link_id: 0d9c8b7a-6e5f-4d3c-b2a1-908f7e6d5c4b
                scheduled_for: '2026-08-14T06:00:00+00:00'
                timezone: Asia/Jerusalem
                status: scheduled
                attempt_count: 0
                next_attempt_at: null
                last_error_code: null
                created_at: '2026-08-10T09:00:00+00:00'
                updated_at: '2026-08-10T09:00:00+00:00'
        '400':
          description: '`validation_failed` — The request failed schema validation. See error.details for field messages.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '401':
          description: '`invalid_credentials` — The credential is malformed or does not match an active key. The response never reveals whether a key identifier exists. `missing_credentials` — No Authorization: Bearer header was sent. `revoked_credentials` — The presented key was revoked. Create a new key in Settings → API.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '403':
          description: '`feature_unavailable` — The tenant''s tier does not currently include API access. Feature rollout flags cannot grant this entitlement. `permission_denied` — The key''s permission does not allow this operation (readonly keys cannot call admin-only operations).'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '404':
          description: '`not_found` — No such resource in YOUR tenant. Ids belonging to another tenant are indistinguishable from missing ones.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '409':
          description: '`conflict` — A transient state conflict: an identical request is still in flight, a scheduled email is being sent, or a sync is already running. Retry after a short wait (honour Retry-After when present).'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '413':
          description: '`payload_too_large` — The request body exceeds the 64 KiB limit.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '429':
          description: '`rate_limit_exceeded` — Too many requests — for this key, for the workspace (one budget shared by all its API keys and MCP agent connections), or, on a heavy operation, for the workspace''s heavy budget. Honor the Retry-After header (seconds) before retrying — waiting that long is enough. The RateLimit-* fields on every response say how much headroom is left before this happens; pace against them instead of retrying into a refusal.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '500':
          description: '`internal_error` — Unexpected server error. Safe to retry with the same Idempotency-Key.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                scheduled_local:
                  type: string
                  description: New tenant-local YYYY-MM-DDTHH:mm send time.
                  pattern: ^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}$
              required:
              - scheduled_local
              additionalProperties: false
            example:
              scheduled_local: 2026-08-15T09:30
    delete:
      operationId: deleteScheduledEmail
      summary: Delete a scheduled payment-link email
      description: 'Cancels an email whose status is `scheduled` or `failed`: the customer will NOT be emailed about this payment link (its email mode becomes suppress); the link itself stays payable. A `processing` email cannot be cancelled because the provider outcome may already be in flight. A repeat answers `not_found` (already gone).


        Requires an **admin** API key (has side effects).'
      tags:
      - Scheduler
      security:
      - apiKey: []
      x-permission: admin
      x-side-effects: true
      parameters:
      - name: scheduledEmailId
        in: path
        required: true
        description: Scheduled email id.
        schema:
          type: string
          format: uuid
      responses:
        '200':
          description: Success
          content:
            application/json:
              schema:
                type: object
                properties:
                  id:
                    type: string
                    description: Deleted scheduled email id.
                    format: uuid
                  deleted:
                    type: boolean
                    description: True when the schedule was deleted.
                required:
                - id
                - deleted
                additionalProperties: false
              example:
                id: 22222222-2222-4222-8222-222222222222
                deleted: true
        '400':
          description: '`validation_failed` — The request failed schema validation. See error.details for field messages.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '401':
          description: '`invalid_credentials` — The credential is malformed or does not match an active key. The response never reveals whether a key identifier exists. `missing_credentials` — No Authorization: Bearer header was sent. `revoked_credentials` — The presented key was revoked. Create a new key in Settings → API.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '403':
          description: '`feature_unavailable` — The tenant''s tier does not currently include API access. Feature rollout flags cannot grant this entitlement. `permission_denied` — The key''s permission does not allow this operation (readonly keys cannot call admin-only operations).'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '404':
          description: '`not_found` — No such resource in YOUR tenant. Ids belonging to another tenant are indistinguishable from missing ones.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '409':
          description: '`conflict` — A transient state conflict: an identical request is still in flight, a scheduled email is being sent, or a sync is already running. Retry after a short wait (honour Retry-After when present).'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '413':
          description: '`payload_too_large` — The request body exceeds the 64 KiB limit.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '429':
          description: '`rate_limit_exceeded` — Too many requests — for this key, for the workspace (one budget shared by all its API keys and MCP agent connections), or, on a heavy operation, for the workspace''s heavy budget. Honor the Retry-After header (seconds) before retrying — waiting that long is enough. The RateLimit-* fields on every response say how much headroom is left before this happens; pace against them instead of retrying into a refusal.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '500':
          description: '`internal_error` — Unexpected server error. Safe to retry with the same Idempotency-Key.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
components:
  schemas:
    Error:
      type: object
      required:
      - error
      additionalProperties: false
      properties:
        error:
          type: object
          required:
          - code
          - message
          - request_id
          additionalProperties: false
          properties:
            code:
              type: string
              description: Stable machine-readable error code.
              enum:
              - missing_credentials
              - invalid_credentials
              - revoked_credentials
              - feature_unavailable
              - permission_denied
              - not_found
              - invalid_cursor
              - validation_failed
              - conflict
              - link_already_covered
              - link_not_withdrawable
              - payment_link_deactivated
              - idempotency_key_required
              - idempotency_conflict
              - rate_limit_exceeded
              - payload_too_large
              - method_not_allowed
              - installment_not_collectible
              - amount_exceeds_available
              - sync_disabled
              - connection_disconnected
              - integration_unavailable
              - currency_not_supported
              - internal_error
            message:
              type: string
              description: Human-readable explanation.
            request_id:
              type: string
              description: Echo of X-Request-Id — include it when reporting problems.
            details:
              type: array
              description: Field-level validation messages (validation_failed only).
              items:
                type: object
                required:
                - field
                - message
                properties:
                  field:
                    type: string
                  message:
                    type: string
  securitySchemes:
    apiKey:
      type: http
      scheme: bearer
      description: 'A Finero API key (`fnr_…`), created by a tenant admin in Settings → API. Sent as `Authorization: Bearer fnr_…`. Never place keys in URLs.'
      x-permissions:
        supported:
        - admin
        - readonly
        operationField: x-permission
        description: 'Each operation declares the LEAST key permission that may call it in `x-permission`. An `admin` key satisfies every operation; a `readonly` key satisfies only operations whose `x-permission` is `readonly`. The router enforces this from the same classification this document is generated from — the check is never inferred from the HTTP method. These are key PERMISSIONS, not OAuth scopes: this scheme is HTTP bearer, and OpenAPI declares `scopes` only on `oauth2`/`openIdConnect` schemes. Finero''s OAuth surface is the MCP server, which publishes RFC 9728 protected-resource metadata at `https://app.getfinero.com/mcp/.well-known/oauth-protected-resource`. Access there is granted on a consent screen, where a person lets the agent act as them, and every call is checked against that person''s own permissions — not by OAuth scope values; the authorization server accepts only `openid`.'
externalDocs:
  description: Agent-facing orientation, including the MCP server and how to connect one.
  url: https://app.getfinero.com/llms.txt