Skip to content

Create an expense

POST
/api/expenses
curl --request POST \
--url https://invoicing-api.eddish.app/api/expenses \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '{ "projectId": "2489E9AD-2EE2-8E00-8EC9-32D5F69181C0", "expenseCategoryId": "2489E9AD-2EE2-8E00-8EC9-32D5F69181C0", "spentDate": "2026-04-15", "amount": 1, "quantity": 1, "notes": "example", "billable": true, "reimbursable": false }'

Creates an expense. A category priced per unit works out the amount from the quantity.

Media typeapplication/json

The details of a new expense.

object
projectId
required

The project to put the expense on. Leave empty for none; an expense with no project is never billable.

null | string format: uuid
expenseCategoryId
required
string format: uuid
spentDate
required

The day the money was spent, as yyyy-MM-dd.

string format: date
amount
required

The amount, in the account’s currency. Worked out from the quantity for a per-unit category.

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

How many units, for a per-unit category. Leave empty otherwise.

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

Whether the money is owed back to the person who spent it.

boolean

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": {
"reimbursable": false,
"approvalState": "Open",
"canDecide": false
}
}

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.