Developers · Recurring schedule reference

Recurring schedule reference

Adding a schedule object to the expense creation payload turns a one-time expense into a recurring template. The engine generates virtual instances automatically for each month view.

← Back to API reference

Schedule object fields

Field Type Default Description
frequencystringNoneRequired. One of: once, daily, weekly, monthly, quarterly, yearly.
startDatestring | integerNoneRequired. ISO date ("2026-03-01") or Unix seconds (1740787200).
endDatestring | integernoneHard end date. Cannot be combined with endAfter.
dayOfMonthintegernoneDay of month (1–31). Used with monthly / quarterly, or paired with monthOfYear for yearly.
monthOfYearintegernoneMonth 1–12. Only for yearly, together with dayOfMonth.
dayOfWeekintegernoneDay of week (0 = Sunday … 6 = Saturday). Used with weekly or monthly+weekOfMonth.
daysOfWeekinteger[]noneMultiple days (e.g. [1, 3, 5] = Mon/Wed/Fri). weekly only.
weekOfMonthintegernoneWeek number (1–5). Requires dayOfWeek. Used with monthly / quarterly.
intervalinteger1Repeat every N periods (1–100). E.g. 2 with weekly = every 2 weeks.
endAfterobjectnoneEnd condition. Cannot be combined with endDate. See below.
timezonestring"Europe/Warsaw"IANA timezone for occurrence calculation.

endAfter object

type value Description
"count"integer (1–1000)Stop after N total occurrences.
"date"ISO date or Unix secondsStop on/after this date.
End conditions are mutually exclusive
You can use endDate or endAfter, but not both. Omit both for an open-ended (infinite) recurring schedule.

Required fields by frequency

frequency Required extras Allowed optional Forbidden
oncenonenonedayOfMonth, dayOfWeek, daysOfWeek, weekOfMonth, interval ≠ 1
dailynoneintervaldayOfMonth, dayOfWeek, daysOfWeek, weekOfMonth
weeklydayOfWeek or daysOfWeekintervaldayOfMonth, weekOfMonth
monthlydayOfMonth or (weekOfMonth + dayOfWeek)intervaldaysOfWeek
quarterlydayOfMonth or (weekOfMonth + dayOfWeek)intervaldaysOfWeek
yearlynone (legacy startDate rule)interval, monthOfYear + dayOfMonthdaysOfWeek
weekOfMonth always requires dayOfWeek
To express patterns like “first Monday” or “third Friday”, you must provide both weekOfMonth and dayOfWeek.

frequency: once

A single scheduled occurrence at startDate. Useful for a future-dated expense that should appear on its due date.

curl -X POST https://api.figa.cc/api/v1/expenses \
  -H "x-api-key: et_live_abc123..." \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Annual insurance premium",
    "amount": 240000,
    "categoryInput": "Insurance",
    "schedule": {
      "frequency": "once",
      "startDate": "2026-06-15"
    }
  }'

frequency: daily

Every day

"schedule": {
  "frequency": "daily",
  "startDate": "2026-03-01"
}

Every 3 days

"schedule": {
  "frequency": "daily",
  "startDate": "2026-03-01",
  "interval": 3
}

Every day for 30 days

"schedule": {
  "frequency": "daily",
  "startDate": "2026-03-01",
  "endAfter": { "type": "count", "value": 30 }
}

Every day until end of year

"schedule": {
  "frequency": "daily",
  "startDate": "2026-03-01",
  "endDate": "2026-12-31"
}

frequency: weekly

Every Friday

"schedule": {
  "frequency": "weekly",
  "startDate": "2026-03-06",
  "dayOfWeek": 5
}
dayOfWeek values
0 = Sunday, 1 = Monday, 2 = Tuesday, 3 = Wednesday, 4 = Thursday, 5 = Friday, 6 = Saturday.

Every other Monday

"schedule": {
  "frequency": "weekly",
  "startDate": "2026-03-02",
  "dayOfWeek": 1,
  "interval": 2
}

Monday, Wednesday, Friday every week

"schedule": {
  "frequency": "weekly",
  "startDate": "2026-03-02",
  "daysOfWeek": [1, 3, 5]
}

Tuesday and Thursday, every 2 weeks, 20 occurrences

"schedule": {
  "frequency": "weekly",
  "startDate": "2026-03-03",
  "daysOfWeek": [2, 4],
  "interval": 2,
  "endAfter": { "type": "count", "value": 20 }
}

Full curl: weekly team lunch

curl -X POST https://api.figa.cc/api/v1/expenses \
  -H "x-api-key: et_live_abc123..." \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Team lunch",
    "amount": 15000,
    "categoryInput": "Food",
    "recipientInput": "Pizza Place",
    "schedule": {
      "frequency": "weekly",
      "startDate": "2026-03-06",
      "dayOfWeek": 5
    }
  }'

frequency: monthly

1st of every month (rent, subscriptions)

"schedule": {
  "frequency": "monthly",
  "startDate": "2026-01-01",
  "dayOfMonth": 1
}

15th of every month

"schedule": {
  "frequency": "monthly",
  "startDate": "2026-01-15",
  "dayOfMonth": 15
}

Last day of month (dayOfMonth: 31)

"schedule": {
  "frequency": "monthly",
  "startDate": "2026-01-31",
  "dayOfMonth": 31
}
dayOfMonth: 31 in short months
When dayOfMonth exceeds the number of days in a month (e.g. 31 in February), the occurrence is placed on the last day of that month.

Every other month on the 10th

"schedule": {
  "frequency": "monthly",
  "startDate": "2026-01-10",
  "dayOfMonth": 10,
  "interval": 2
}

First Monday of every month

"schedule": {
  "frequency": "monthly",
  "startDate": "2026-01-06",
  "weekOfMonth": 1,
  "dayOfWeek": 1
}

Third Friday of every month

"schedule": {
  "frequency": "monthly",
  "startDate": "2026-01-16",
  "weekOfMonth": 3,
  "dayOfWeek": 5
}

12 monthly installments (loan repayment)

"schedule": {
  "frequency": "monthly",
  "startDate": "2026-01-01",
  "dayOfMonth": 1,
  "endAfter": { "type": "count", "value": 12 }
}

Monthly until December 2027

"schedule": {
  "frequency": "monthly",
  "startDate": "2026-01-01",
  "dayOfMonth": 1,
  "endDate": "2027-12-31"
}

Full curl: monthly rent

curl -X POST https://api.figa.cc/api/v1/expenses \
  -H "x-api-key: et_live_abc123..." \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Office rent",
    "amount": 420000,
    "categoryInput": "Housing",
    "recipientInput": "WeWork",
    "description": "Monthly office lease",
    "currency": "PLN",
    "schedule": {
      "frequency": "monthly",
      "startDate": "2026-01-01",
      "dayOfMonth": 1
    }
  }'

Full curl: 36 installments (phone lease)

curl -X POST https://api.figa.cc/api/v1/expenses \
  -H "x-api-key: et_live_abc123..." \
  -H "Content-Type: application/json" \
  -d '{
    "name": "iPhone lease",
    "amount": 12500,
    "categoryInput": "Equipment",
    "recipientInput": "Apple",
    "schedule": {
      "frequency": "monthly",
      "startDate": "2026-02-15",
      "dayOfMonth": 15,
      "endAfter": { "type": "count", "value": 36 }
    }
  }'

frequency: quarterly

1st of each quarter

"schedule": {
  "frequency": "quarterly",
  "startDate": "2026-01-01",
  "dayOfMonth": 1
}

15th of each quarter

"schedule": {
  "frequency": "quarterly",
  "startDate": "2026-01-15",
  "dayOfMonth": 15
}

First Monday of each quarter

"schedule": {
  "frequency": "quarterly",
  "startDate": "2026-01-05",
  "weekOfMonth": 1,
  "dayOfWeek": 1
}

Full curl: quarterly tax advance

curl -X POST https://api.figa.cc/api/v1/expenses \
  -H "x-api-key: et_live_abc123..." \
  -H "Content-Type: application/json" \
  -d '{
    "name": "VAT advance payment",
    "amount": 850000,
    "categoryInput": "Tax",
    "recipientInput": "Urząd Skarbowy",
    "schedule": {
      "frequency": "quarterly",
      "startDate": "2026-01-25",
      "dayOfMonth": 25
    }
  }'

frequency: yearly

Yearly on start date anniversary

"schedule": {
  "frequency": "yearly",
  "startDate": "2026-06-15"
}

Without monthOfYear, the yearly month and day come from startDate; a legacy dayOfMonth alone remains ignored. With monthOfYear (1–12), dayOfMonth (1–31) is required and defines the yearly date. The start still determines the interval phase and earliest generated term. The pair is available through the same schedule schema in the session API, API-key API and existing MCP schedule edits.

Every 2 years

"schedule": {
  "frequency": "yearly",
  "startDate": "2026-06-15",
  "interval": 2
}

Preserve February 29 after extending history

{
  "frequency": "yearly",
  "startDate": "2023-03-01",
  "monthOfYear": 2,
  "dayOfMonth": 29
}

This produces March 1 in 2023 and February 29 in 2024 and 2028. February 29 overflows to March 1 in non-leap years, preserving the existing yearly rule. A range backfill from a legacy February 29 anchor records this pair automatically. Reading an old schedule does not activate previously ignored fields.

Full curl: yearly domain renewal

curl -X POST https://api.figa.cc/api/v1/expenses \
  -H "x-api-key: et_live_abc123..." \
  -H "Content-Type: application/json" \
  -d '{
    "name": "figa.io domain renewal",
    "amount": 4500,
    "categoryInput": "Infrastructure",
    "recipientInput": "Cloudflare",
    "currency": "USD",
    "schedule": {
      "frequency": "yearly",
      "startDate": "2026-09-12"
    }
  }'

Managing recurring expenses

To change fixed/variable monthly billing from an occurrence, use recurringSettings. Save calendar changes separately. Schedule changes are unavailable for series with billing-method history.

Read schedule details

curl -X GET https://api.figa.cc/api/v1/expenses/{expenseId} \
  -H "x-api-key: et_live_abc123..."

Returns the full expense object with schedule, occurrences array, and stats (totalOccurrences, paidOccurrences, progress, etc.).

Read a single occurrence

curl -X GET "https://api.figa.cc/api/v1/expenses/{expenseId}?occurrence=1740787200" \
  -H "x-api-key: et_live_abc123..."

Pass the occurrence's scheduled Unix timestamp to get that specific instance.

Change schedule for entire series

curl -X PUT https://api.figa.cc/api/v1/expenses/{expenseId} \
  -H "x-api-key: et_live_abc123..." \
  -H "Content-Type: application/json" \
  -d '{
    "schedule": {
      "frequency": "monthly",
      "startDate": "2026-01-05",
      "dayOfMonth": 5
    },
    "editScope": "ALL_INSTANCES"
  }'

Update amount for this + future occurrences

curl -X PUT https://api.figa.cc/api/v1/expenses/{expenseId} \
  -H "x-api-key: et_live_abc123..." \
  -H "Content-Type: application/json" \
  -d '{
    "amount": 450000,
    "editScope": "FUTURE_INSTANCES"
  }'

Override a single occurrence

curl -X PUT https://api.figa.cc/api/v1/expenses/{expenseId} \
  -H "x-api-key: et_live_abc123..." \
  -H "Content-Type: application/json" \
  -d '{
    "amount": 500000,
    "description": "Increased rent for March",
    "editScope": "THIS_INSTANCE"
  }'

Skip an occurrence

scheduledDate accepts Unix seconds or ISO date YYYY-MM-DD.

Using Unix timestamp

curl -X POST https://api.figa.cc/api/v1/expenses/{expenseId}/skip/toggle \
  -H "x-api-key: et_live_abc123..." \
  -H "Content-Type: application/json" \
  -d '{"scheduledDate": 1740787200}'

Using ISO date (recommended)

curl -X POST https://api.figa.cc/api/v1/expenses/{expenseId}/skip/toggle \
  -H "x-api-key: et_live_abc123..." \
  -H "Content-Type: application/json" \
  -d '{"scheduledDate": "2026-03-30"}'

Call again with the same scheduledDate to restore a skipped occurrence. Only unpaid instances can be skipped. If occurrence is not found, API returns 404.

Convert recurring to one-time

curl -X PUT https://api.figa.cc/api/v1/expenses/{expenseId} \
  -H "x-api-key: et_live_abc123..." \
  -H "Content-Type: application/json" \
  -d '{
    "schedule": null,
    "editScope": "ALL_INSTANCES"
  }'

Delete single occurrence

curl -X DELETE "https://api.figa.cc/api/v1/expenses/{expenseId}?editScope=THIS_INSTANCE" \
  -H "x-api-key: et_live_abc123..."

Delete entire series

curl -X DELETE "https://api.figa.cc/api/v1/expenses/{expenseId}?editScope=ALL_INSTANCES" \
  -H "x-api-key: et_live_abc123..."

editScope reference

When updating (PUT) or deleting (DELETE) a recurring expense, editScope determines which occurrences are affected:

Value PUT effect DELETE effect
THIS_INSTANCEMaterializes and overrides only this occurrence.Removes only this occurrence.
FUTURE_INSTANCESApplies changes to this and all future occurrences.Removes this and all future occurrences.
ALL_INSTANCESUpdates the template (all past + future).Deletes the entire recurring series.
editScope is for recurring expenses only
For one-time expenses, omit editScope. It is ignored for non-recurring expenses.

Quick reference: common patterns

Use case schedule JSON
Monthly rent on 1st { "frequency": "monthly", "startDate": "2026-01-01", "dayOfMonth": 1 }
Weekly Friday coffee { "frequency": "weekly", "startDate": "2026-01-02", "dayOfWeek": 5 }
Quarterly tax (25th) { "frequency": "quarterly", "startDate": "2026-01-25", "dayOfMonth": 25 }
Yearly renewal { "frequency": "yearly", "startDate": "2026-09-12" }
Bi-weekly Mon payroll { "frequency": "weekly", "startDate": "2026-01-05", "dayOfWeek": 1, "interval": 2 }
Mon/Wed/Fri gym { "frequency": "weekly", "startDate": "2026-01-05", "daysOfWeek": [1, 3, 5] }
1st Monday of month { "frequency": "monthly", "startDate": "2026-01-05", "weekOfMonth": 1, "dayOfWeek": 1 }
12 installments { "frequency": "monthly", "startDate": "2026-01-01", "dayOfMonth": 1, "endAfter": { "type": "count", "value": 12 } }
Daily until end of year { "frequency": "daily", "startDate": "2026-01-01", "endDate": "2026-12-31" }
Every 3 days, 30 times { "frequency": "daily", "startDate": "2026-01-01", "interval": 3, "endAfter": { "type": "count", "value": 30 } }
Was this helpful?