Skip to content

Create a recurring schedule

POST
/api/recurring-schedules
curl --request POST \
--url https://invoicing-api.eddish.app/api/recurring-schedules \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '{ "clientId": "2489E9AD-2EE2-8E00-8EC9-32D5F69181C0", "intervalCount": 1, "intervalUnit": "Day", "firstIssueDate": "2026-04-15", "endMode": "Never", "endDate": "2026-04-15", "maxOccurrences": 1, "inclusions": "Manual", "projectIds": [ "2489E9AD-2EE2-8E00-8EC9-32D5F69181C0" ], "groupBy": "Project", "includeProject": true, "includeTask": true, "includePeople": true, "includeDate": true, "includeNotes": true, "purchaseOrder": "example", "subjectTemplate": "example", "notesTemplate": "example", "paymentTerm": "UponReceipt", "customPaymentTermDays": 1, "customPaymentTermUnit": "Day", "tax": 1, "discount": 1, "showHoursSummary": true, "periodStart": "2026-04-15", "periodEnd": "2026-04-15" }'

Creates a recurring invoice schedule and its empty template invoice. The first invoice is issued on the schedule’s first issue date, once the template has line items. Admins and Finance only.

Media typeapplication/json

The details of a new recurring invoice schedule. Creating it also creates its empty template invoice.

object
clientId
required
string format: uuid
intervalCount
required

How many interval units pass between invoices: 2 with Week means every two weeks.

integer | string format: int32
/^-?(?:0|[1-9]\d*)$/
intervalUnit
required

Day, Week, Month or Year.

Allowed values: Day Week Month Year
firstIssueDate
required

The date the first invoice is issued, as yyyy-MM-dd.

string format: date
endMode
required

When the schedule stops issuing: Never (until someone stops it), OnDate (no invoice is issued after EndDate) or AfterOccurrences (once MaxOccurrences invoices have been issued).

Allowed values: Never OnDate AfterOccurrences
endDate
required

With OnDate, the last date an invoice can be issued, as yyyy-MM-dd.

null | string format: date
maxOccurrences
required

With AfterOccurrences, how many invoices to issue in all.

null | integer | string format: int32
/^-?(?:0|[1-9]\d*)$/
inclusions
required

What the invoices are built from: Manual, Time, Expenses or TimeAndExpenses.

Allowed values: Manual Time Expenses TimeAndExpenses
projectIds
required

The projects to bill.

Array<string>
groupBy
required

How lines are grouped: Project, Task, Person or Detailed.

Allowed values: Project Task Person Detailed
includeProject
required
boolean
includeTask
required
boolean
includePeople
required
boolean
includeDate
required
boolean
includeNotes
required
boolean
purchaseOrder
required
null | string
subjectTemplate
required

The subject, which may contain date placeholders.

null | string
notesTemplate
required

The notes, which may contain date placeholders.

null | string
paymentTerm
required
One of:
null
customPaymentTermDays
required

The length of a Custom payment term, counted in the custom unit.

null | integer | string format: int32
/^-?(?:0|[1-9]\d*)$/
customPaymentTermUnit
required
One of:
null
tax
required

The tax rate, as a percentage.

null | number | string format: double
/^-?(?:0|[1-9]\d*)(?:\.\d+)?$/
discount
required

The discount, as a percentage.

null | number | string format: double
/^-?(?:0|[1-9]\d*)(?:\.\d+)?$/
showHoursSummary
required

Whether invoices print a summary of hours.

boolean
periodStart
required

Not used. Ignored if sent.

null | string format: date
periodEnd
required

Not used. Ignored if sent.

null | string format: date

OK

Media typeapplication/json

Shorthand for the overwhelmingly common ServiceResponse&lt;TData, ErrorResponse&gt;. C# cannot alias an open generic, so this is expressed as a subclass rather than a using alias.

object
success

True when the call succeeded and TData? ServiceResponse<TData, TError>.Data is populated.

boolean
data
One of:
null
error
One of:
null

Example

{
"data": {
"schedule": {
"intervalUnit": "Day",
"endMode": "Never",
"inclusions": "Manual",
"groupBy": "Project",
"paymentTerm": "UponReceipt",
"customPaymentTermUnit": "Day"
},
"template": {
"state": "Draft",
"paymentTerm": "UponReceipt",
"customPaymentTermUnit": "Day"
}
}
}

Not signed in, or the credential isn’t recognised, has expired or was revoked: auth.missing_credential, auth.invalid_credential, auth.expired_credential, auth.revoked_credential.

Media typeapplication/json

Shorthand for the overwhelmingly common ServiceResponse&lt;TData, ErrorResponse&gt;. C# cannot alias an open generic, so this is expressed as a subclass rather than a using alias.

object
success

True when the call succeeded and TData? ServiceResponse<TData, TError>.Data is populated.

boolean
data

Payload on success; default on failure.

error

The Framework’s default error shape, used as the TError of ServiceResponse<TData, TError> unless an App genuinely needs its own.

object
code

Stable, machine-readable error identifier (for example account.not_found). Clients branch on this, never on string ErrorResponse.Message.

string
message

Human-readable description of what went wrong. Safe to surface to a User.

string
fieldErrors

Optional per-field validation failures, keyed by field name. Null (and omitted from JSON) for errors that are not validation failures.

null | object
details

Optional machine-readable facts about the failure that a client renders rather than parses out of string ErrorResponse.Message — e.g. plan.limit_reached’s limit, count and planName. Null (and omitted from JSON) for errors that carry none.

null | object

Example generated

{
"success": true,
"data": "example",
"error": {
"code": "example",
"message": "example",
"fieldErrors": "example",
"details": "example"
}
}

Not allowed. error.code says why: auth.role_not_permitted (your role doesn’t allow it), auth.read_only_token (the token can only read), app.no_seat (no seat in this App), auth.api_access_not_in_plan (the plan doesn’t include API access), auth.not_a_member (no longer a member of the Account), auth.insufficient_scope (an Account API key without the scope).

Media typeapplication/json

Shorthand for the overwhelmingly common ServiceResponse&lt;TData, ErrorResponse&gt;. C# cannot alias an open generic, so this is expressed as a subclass rather than a using alias.

object
success

True when the call succeeded and TData? ServiceResponse<TData, TError>.Data is populated.

boolean
data

Payload on success; default on failure.

error

The Framework’s default error shape, used as the TError of ServiceResponse<TData, TError> unless an App genuinely needs its own.

object
code

Stable, machine-readable error identifier (for example account.not_found). Clients branch on this, never on string ErrorResponse.Message.

string
message

Human-readable description of what went wrong. Safe to surface to a User.

string
fieldErrors

Optional per-field validation failures, keyed by field name. Null (and omitted from JSON) for errors that are not validation failures.

null | object
details

Optional machine-readable facts about the failure that a client renders rather than parses out of string ErrorResponse.Message — e.g. plan.limit_reached’s limit, count and planName. Null (and omitted from JSON) for errors that carry none.

null | object

Example generated

{
"success": true,
"data": "example",
"error": {
"code": "example",
"message": "example",
"fieldErrors": "example",
"details": "example"
}
}

The Account is over its plan’s limits, so changes are refused until an Owner or Admin sorts that out: plan.read_only.

Media typeapplication/json

Shorthand for the overwhelmingly common ServiceResponse&lt;TData, ErrorResponse&gt;. C# cannot alias an open generic, so this is expressed as a subclass rather than a using alias.

object
success

True when the call succeeded and TData? ServiceResponse<TData, TError>.Data is populated.

boolean
data

Payload on success; default on failure.

error

The Framework’s default error shape, used as the TError of ServiceResponse<TData, TError> unless an App genuinely needs its own.

object
code

Stable, machine-readable error identifier (for example account.not_found). Clients branch on this, never on string ErrorResponse.Message.

string
message

Human-readable description of what went wrong. Safe to surface to a User.

string
fieldErrors

Optional per-field validation failures, keyed by field name. Null (and omitted from JSON) for errors that are not validation failures.

null | object
details

Optional machine-readable facts about the failure that a client renders rather than parses out of string ErrorResponse.Message — e.g. plan.limit_reached’s limit, count and planName. Null (and omitted from JSON) for errors that carry none.

null | object

Example generated

{
"success": true,
"data": "example",
"error": {
"code": "example",
"message": "example",
"fieldErrors": "example",
"details": "example"
}
}

Too many requests: rate_limit.exceeded. Wait the number of seconds in the Retry-After header.

Media typeapplication/json

Shorthand for the overwhelmingly common ServiceResponse&lt;TData, ErrorResponse&gt;. C# cannot alias an open generic, so this is expressed as a subclass rather than a using alias.

object
success

True when the call succeeded and TData? ServiceResponse<TData, TError>.Data is populated.

boolean
data

Payload on success; default on failure.

error

The Framework’s default error shape, used as the TError of ServiceResponse<TData, TError> unless an App genuinely needs its own.

object
code

Stable, machine-readable error identifier (for example account.not_found). Clients branch on this, never on string ErrorResponse.Message.

string
message

Human-readable description of what went wrong. Safe to surface to a User.

string
fieldErrors

Optional per-field validation failures, keyed by field name. Null (and omitted from JSON) for errors that are not validation failures.

null | object
details

Optional machine-readable facts about the failure that a client renders rather than parses out of string ErrorResponse.Message — e.g. plan.limit_reached’s limit, count and planName. Null (and omitted from JSON) for errors that carry none.

null | object

Example generated

{
"success": true,
"data": "example",
"error": {
"code": "example",
"message": "example",
"fieldErrors": "example",
"details": "example"
}
}
Retry-After
integer

Seconds to wait before trying again.