Create a project
const url = 'https://invoicing-api.eddish.app/api/Projects';const options = { method: 'POST', headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'}, body: '{"clientId":"2489E9AD-2EE2-8E00-8EC9-32D5F69181C0","name":"example","code":"example","isActive":true,"isBillable":true,"isFixedFee":true,"billBy":"None","hourlyRate":1,"fee":1,"notes":"example","startsOn":"2026-04-15","endsOn":"2026-04-15","budgetType":"None","budgetAmount":1,"budgetResetsMonthly":false}'};
try { const response = await fetch(url, options); const data = await response.json(); console.log(data);} catch (error) { console.error(error);}curl --request POST \ --url https://invoicing-api.eddish.app/api/Projects \ --header 'Authorization: Bearer <token>' \ --header 'Content-Type: application/json' \ --data '{ "clientId": "2489E9AD-2EE2-8E00-8EC9-32D5F69181C0", "name": "example", "code": "example", "isActive": true, "isBillable": true, "isFixedFee": true, "billBy": "None", "hourlyRate": 1, "fee": 1, "notes": "example", "startsOn": "2026-04-15", "endsOn": "2026-04-15", "budgetType": "None", "budgetAmount": 1, "budgetResetsMonthly": false }'Creates a project for a client. Every active common task is added to it automatically. Replies 409 if the plan’s project limit has been reached. Admins only, or Approvers where the account allows it.
Authorizations
Section titled “Authorizations”Request Bodyrequired
Section titled “Request Bodyrequired”The details to save for a project when creating or updating one.
object
A short code for the project.
False archives the project.
Whether time on the project can be billed.
Whether the project is billed for a fixed fee rather than for hours.
How hourly rates are set: None, Project, Tasks or People.
The project’s hourly rate, in the account’s currency. Only saved for someone who may see everyone’s amounts.
The fixed fee, in the account’s currency. Only saved for someone who may see everyone’s amounts.
The project’s start date, as yyyy-MM-dd.
The project’s end date, as yyyy-MM-dd.
What the budget is measured in: None, Hours or Fees.
The budget: a number of hours, or an amount in the account’s currency, depending on the budget type.
Whether the budget starts over each month.
Responses
Section titled “Responses”OK
Shorthand for the overwhelmingly common ServiceResponse<TData, ErrorResponse>.
C# cannot alias an open generic, so this is expressed as a subclass rather than a using alias.
object
True when the call succeeded and TData? ServiceResponse<TData, TError>.Data is populated.
Payload on success; default on failure.
object
A short code for the project, such as one from the client’s own system.
Whether time on the project can be billed.
Whether the project is billed for a fixed fee rather than for hours.
How hourly rates are set: None, Project, Tasks or People.
The project’s hourly rate, in the account’s currency. Only shown to people who may see everyone’s amounts.
The fixed fee, in the account’s currency. Only shown to people who may see everyone’s amounts.
The project’s start date, as yyyy-MM-dd.
The project’s end date, as yyyy-MM-dd.
What the budget is measured in: None, Hours or Fees.
The budget: a number of hours, or an amount in the account’s currency, depending on the budget type.
Whether the budget starts over each month, as for a monthly retainer.
Hours worked on the project. For a budget that resets monthly, only this month’s.
Billable value worked on the project, over the same period as the used hours. Only shown to people who may see everyone’s amounts.
Total hours worked on the project.
Billable time and expenses not yet on an invoice. Only shown to people who may see everyone’s amounts.
The date of the most recent time entry on the project.
Set when the account’s plan limit archived the project automatically.
Error on failure; default on success. Always serialized, per TData? ServiceResponse<TData, TError>.Data.
object
Stable, machine-readable error identifier (for example account.not_found).
Clients branch on this, never on string ErrorResponse.Message.
Human-readable description of what went wrong. Safe to surface to a User.
Optional per-field validation failures, keyed by field name. Null (and omitted from JSON) for errors that are not validation failures.
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.
Example
{ "data": { "billBy": "None", "budgetType": "None", "budgetResetsMonthly": false, "budgetUsedHours": 0, "hours": 0 }}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.
Shorthand for the overwhelmingly common ServiceResponse<TData, ErrorResponse>.
C# cannot alias an open generic, so this is expressed as a subclass rather than a using alias.
object
True when the call succeeded and TData? ServiceResponse<TData, TError>.Data is populated.
Payload on success; default on failure.
The Framework’s default error shape, used as the TError of
ServiceResponse<TData, TError> unless an App genuinely needs its own.
object
Stable, machine-readable error identifier (for example account.not_found).
Clients branch on this, never on string ErrorResponse.Message.
Human-readable description of what went wrong. Safe to surface to a User.
Optional per-field validation failures, keyed by field name. Null (and omitted from JSON) for errors that are not validation failures.
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.
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).
Shorthand for the overwhelmingly common ServiceResponse<TData, ErrorResponse>.
C# cannot alias an open generic, so this is expressed as a subclass rather than a using alias.
object
True when the call succeeded and TData? ServiceResponse<TData, TError>.Data is populated.
Payload on success; default on failure.
The Framework’s default error shape, used as the TError of
ServiceResponse<TData, TError> unless an App genuinely needs its own.
object
Stable, machine-readable error identifier (for example account.not_found).
Clients branch on this, never on string ErrorResponse.Message.
Human-readable description of what went wrong. Safe to surface to a User.
Optional per-field validation failures, keyed by field name. Null (and omitted from JSON) for errors that are not validation failures.
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.
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.
Shorthand for the overwhelmingly common ServiceResponse<TData, ErrorResponse>.
C# cannot alias an open generic, so this is expressed as a subclass rather than a using alias.
object
True when the call succeeded and TData? ServiceResponse<TData, TError>.Data is populated.
Payload on success; default on failure.
The Framework’s default error shape, used as the TError of
ServiceResponse<TData, TError> unless an App genuinely needs its own.
object
Stable, machine-readable error identifier (for example account.not_found).
Clients branch on this, never on string ErrorResponse.Message.
Human-readable description of what went wrong. Safe to surface to a User.
Optional per-field validation failures, keyed by field name. Null (and omitted from JSON) for errors that are not validation failures.
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.
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.
Shorthand for the overwhelmingly common ServiceResponse<TData, ErrorResponse>.
C# cannot alias an open generic, so this is expressed as a subclass rather than a using alias.
object
True when the call succeeded and TData? ServiceResponse<TData, TError>.Data is populated.
Payload on success; default on failure.
The Framework’s default error shape, used as the TError of
ServiceResponse<TData, TError> unless an App genuinely needs its own.
object
Stable, machine-readable error identifier (for example account.not_found).
Clients branch on this, never on string ErrorResponse.Message.
Human-readable description of what went wrong. Safe to surface to a User.
Optional per-field validation failures, keyed by field name. Null (and omitted from JSON) for errors that are not validation failures.
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.
Example generated
{ "success": true, "data": "example", "error": { "code": "example", "message": "example", "fieldErrors": "example", "details": "example" }}Headers
Section titled “Headers”Seconds to wait before trying again.