Skip to main content

Create a draft invoice

POSThttps://api.fieldtask.example/public/v1/invoices
Permissions
invoices:create, invoices:read, customers:read, products:read
Safe retries
Idempotency-Key
Also available as
fieldtask_create_invoice

Creates one draft invoice for an existing customer, priced from your product and service catalog, with optional taxes and a discount.

A draft invoice is only saved in FieldTask. Creating it doesn't send it to the customer, doesn't collect or record a payment, and doesn't link it to a job or copy a job's items. The invoice stays a draft until someone in your organization sends it from FieldTask.

The invoice, its line items, taxes and discount are saved together or not at all. This endpoint only creates: it never matches or updates an existing invoice. If you send the same body twice without the same Idempotency-Key, you get two invoices. See Idempotency and safe retries.

Preview

This page is generated from contract 2026-11-03-review.2, a review export that hasn't been released.

Before you start​

You need:

  1. API access for your organization. An owner or admin requests it in Settings → API connections. Until FieldTask enables it, every request returns 403 api_access_not_enabled.
  2. Invoicing enabled for your organization. If your organization can't use invoices in FieldTask, every invoice request returns 404 resource_not_found.
  3. A REST credential with the permissions below: an API token created in Settings → API connections, or an access token from an OAuth client-credentials connection. Tokens from an MCP sign-in don't work with the REST API.
  4. The ID of an existing customer. Find it with GET /public/v1/customers, or create the customer first with POST /public/v1/customers. Archived customers can be used.
  5. The product and price IDs for every line item. Find them with GET /public/v1/products. Each line item is priced from the catalog; you can't send your own prices.

Permissions​

Every request needs all of these permissions:

PermissionDependency in the permission map
invoices:createDepends on customers:read, invoices:read, products:read
invoices:readDepends on customers:read
customers:readNo dependencies
products:readNo dependencies

No input needs further permissions. REST and MCP connections can both apply taxes and a discount.

Adding a permission to a connection doesn't happen automatically. An owner or admin edits it in Settings → API connections. API tokens keep the same token string when their permissions change. OAuth integrations must also request the new scope when they get an access token.

Request​

Responses​

Created

Response Headers
    Cache-Control

    Responses must not be cached.

    Idempotency-Key

    The operation key used for this create, including a server-generated key when omitted.

    Idempotency-Replayed
    Location
    Operation-Location
    X-Request-ID

    Server-generated request correlation ID.

Field guide​

The request body is a single JSON object. customer_id, issued_date and items are required.

FieldRequiredWhat to send
customer_idYesID of a customer in this organization. Archived customers are accepted.
issued_dateYesThe invoice date, such as 2026-11-03. See Dates.
itemsYes1–100 catalog line items. See Line items.
due_dateNoThe payment due date, such as 2026-12-03. Defaults to null.
nameNoA label for the invoice, up to 256 characters. Returned as name. Defaults to null.
subjectNoUp to 256 characters. Saved with the invoice but not returned by the API.
po_numberNoYour customer's purchase order number, up to 256 characters. Saved with the invoice but not returned by the API.
notesNoUp to 1,024 characters. Saved with the invoice but not returned by the API.
tax1, tax2NoUp to two named taxes, each {"name": "…", "type": "percent" or "fixed", "amount": "…"}. See Taxes.
discountNo{"type": "percent" or "fixed", "amount": "…"}. A percentage can't be more than 100. See Totals and discounts.

Anything else in the body is rejected with 422 validation_failed, including fields you might expect to set such as status, invoice_number, currency, job_id or a custom unit price. The organization and the currency always come from your credential's organization, and FieldTask assigns the next invoice number automatically.

Decimal values are JSON strings with up to 2 decimal places, never numbers. Send "8.25", not 8.25.

Body and header rules​

  • Send Content-Type: application/json with an uncompressed body. A Content-Encoding header is rejected with 415.
  • The body can be at most 256 KiB and nest at most 10 levels deep.
  • Duplicate property names, NaN and Infinity are rejected with 400 invalid_json.
  • The URL must have no query string and no trailing slash.

Line items​

Each entry in items adds one product or service from your catalog to the invoice.

Item fieldRequiredWhat to send
product_idYesID of a product or service from GET /public/v1/products.
price_idSometimesID of one of the product's named prices. Required when more than one price option applies.
price_sourceNoprice or legacy. Send legacy to use the product's legacy unit price explicitly.
quantityNoA positive decimal string with up to 8 whole digits and 2 decimal places, such as "1" or "2.50". Zero isn't allowed. Defaults to "1.00".
tax1Notrue to apply the invoice's tax1 to this item. Defaults to false.
tax2Notrue to apply the invoice's tax2 to this item. Defaults to false.
  • The product must be active and available in your catalog. A product marked unavailable is rejected.
  • Only prices in your organization's currency are considered. If no price option matches your selection, or more than one does, the request is rejected with 422 validation_failed rather than FieldTask choosing one for you. Send price_id whenever a product has more than one price.
  • The item's name, description, type and unit price are copied from the catalog when the invoice is created. Later catalog changes don't change the invoice. A product whose description is longer than 1,200 characters can't be added to an invoice.
  • If any item is invalid, no invoice is created.

Taxes​

An invoice can have up to two taxes, tax1 and tax2. Each has a name (1–64 characters, such as "Sales tax"), a type and an amount:

  • "type": "percent": amount is a percentage of each flagged item's amount. "8.25" means 8.25%.
  • "type": "fixed": amount is added once for each flagged item, not once per invoice.

A tax applies only to the items where you set "tax1": true or "tax2": true. Flagging an item with a tax the invoice doesn't define is rejected with 422 validation_failed. Taxes aren't compounded: both are calculated on the item's amount before tax.

Totals, taxes and discounts​

FieldTask calculates the invoice's amounts; you can't send them. For each item:

  1. amount is quantity × unit price, rounded to cents.
  2. tax1_amount and tax2_amount are the item's taxes, each rounded to cents, or "0.00" when the item isn't flagged.
  3. taxed_amount is the amount plus both taxes.

For the invoice:

Response fieldMeaning
total_tax1, total_tax2The sum of each tax over the items.
total_amountThe sum of the items' taxed amounts: the total including tax, before the discount.
discountThe discount you sent, or null. A percentage discount is taken from total_amount, including tax.
discounted_amountThe final invoice total: total_amount minus the discount, rounded to cents.
paid_amountThe sum of recorded payments. Always "0.00" for a new draft.
balancediscounted_amount minus paid_amount.

Rounding uses round-half-to-even at each step, so a value exactly halfway rounds to the nearest even cent. For example, 8.25% of 370.00 is 30.525, which becomes "30.52". Quotes round halves up instead, so the same figures give "30.53" on a quote. Display the returned values rather than recalculating them, and parse them as decimals, not floating-point numbers.

tax1, tax2 and discount in the response show what's stored: their type and both percent_amount and fixed_amount, where the one that doesn't apply is "0.00".

Dates​

issued_date and due_date are calendar dates in YYYY-MM-DD format, with no time or time zone. FieldTask stores them exactly as sent. List filters use issued_date. See Dates and times.

The draft you get back​

A successful create returns the invoice with every field, including its items, taxes and discount:

  • status is draft.
  • invoice_number is assigned by FieldTask. You can't choose it.
  • currency is your organization's currency.
  • customer summarizes the customer.

Read the invoice again later with GET /public/v1/invoices/{id}, using the ID from data.id or the Location header.

Idempotency and safe retries​

Networks fail after a request is sent. The Idempotency-Key header lets you retry without creating a duplicate invoice.

  1. Before sending, generate a new key for the invoice, such as a UUID, and save it with your own record.
  2. Send the request with that key.
  3. If you get no response, a timeout or a 5xx, send the same body with the same key again, with backoff.
SituationResult
New keyThe invoice is created. Response 201, Idempotency-Replayed: false.
Same key, same body, within 7 daysThe original result is returned again. No second invoice is created and no further allowance is used. Response 201, Idempotency-Replayed: true.
Same key, different body409 idempotency_key_reused. Nothing is created.
Same key while the first request is still running409 operation_in_progress with Retry-After. Retry with the same key and body.
Same key more than 7 days later409 idempotency_window_expired. A key can never be reused for a different invoice.

A replay returns the original response, not the invoice's current state. If the customer in that response is no longer visible to the connection, the replay is rejected with 403 operation_forbidden; if the invoice has been deleted, with 410 resource_gone.

Invoices have no external_reference, so keep the returned data.id with your own record. See Idempotency and recovery for key scope, what counts as the same body, and how replays are authorized.

Local creation and external synchronization​

A 201 response means the draft invoice is saved in FieldTask. Nothing further is needed for it to exist, and follow-up work never removes it.

If your organization creates invoices in a connected CRM, FieldTask also sends the new draft there after it's saved. That follow-up only creates the record in the connected CRM. It doesn't send the invoice to the customer, record a payment or attach files. Its progress is reported on the operation, not on the invoice.

To check it, read the Operation-Location URL from the 201 response with the same credential. It needs invoices:read and customers:read, which the create already requires:

curl "$FIELDTASK_API_ORIGIN/public/v1/operations/OPERATION_ID" \
--header "Authorization: Bearer $FIELDTASK_API_TOKEN"

data.local_status is always committed. data.integration.status is not_applicable when your organization doesn't create invoices in a connected CRM. For the other values, see Local creation and external synchronization.

FieldTask makes one attempt to create the invoice in the connected CRM:

  • It's reported as blocked with configuration_required when an item uses a fixed tax. The connected CRM can't represent a fixed per-item tax, and FieldTask won't publish a lower total. Use percentage taxes if the invoice must be synchronized.
  • It's reported as blocked with reconciliation_required when the invoice was changed in FieldTask or is no longer a draft before synchronization ran, the customer was removed, or the customer is linked to more than one contact in the connected CRM.
  • It's reported as failed with provider_unavailable when a request to the connected CRM failed before the invoice was sent.
  • It's reported as outcome_unknown when the request reached the connected CRM but FieldTask couldn't confirm the result. Check there before doing anything that could create a duplicate.
Pending confirmation

In the review contract, invoices created through the API aren't sent to a connected CRM. If your organization creates invoices in a connected CRM, the operation reports a blocked target with configuration_required. The invoice itself is still created.

Never create another invoice under a new key to force synchronization. That creates a second invoice in FieldTask.

Operation results are kept for seven days. After that, GET /public/v1/operations/{id} returns 410 operation_result_expired. That doesn't mean the invoice wasn't created.

Recovering from failures​

What happenedWhat to do
Timeout, connection error or 5xxRetry the same body with the same key, with exponential backoff.
409 operation_in_progressWait for Retry-After seconds and retry with the same key.
429Wait for Retry-After seconds and retry with the same key.
201 receivedStore data.id, data.invoice_number and the Location and Operation-Location headers. Stop retrying.
Key older than 7 days, or the result expiredDon't create again yet. Look for the invoice with GET /public/v1/invoices?customer_id=…&status=draft or your saved invoice ID.

Never switch to a new key just because a response was lost. A new key means a new invoice.

Errors​

Errors use application/problem+json. The top-level code is the stable machine-readable value; error adds a readable message, a suggested fix and a documentation link:

{
"type": "about:blank",
"title": "Unprocessable Entity",
"status": 422,
"code": "validation_failed",
"detail": "The request contains invalid fields.",
"instance": "urn:uuid:99999999-9999-4999-8999-999999999993",
"request_id": "99999999-9999-4999-8999-999999999993",
"error": {
"code": "validation_failed",
"message": "The request contains invalid fields.",
"fix": "Check the documented request fields.",
"docs_url": "https://api.fieldtask.example/public/docs/"
}
}

error.code usually equals code. For a missing permission, code is insufficient_scope and error.code is missing_permission. Validation errors don't say which field failed, and error responses never echo submitted values.

StatuscodeMeaningWhat to do
400invalid_queryThe URL has a query string. Create requests accept none.Send the request to /public/v1/invoices with no query parameters.
400invalid_jsonThe body isn't one valid JSON value, repeats a property name, uses NaN/Infinity or nests deeper than 10 levels.Send a single JSON object encoded as UTF-8.
400invalid_operation_keyIdempotency-Key isn't 16–128 printable ASCII characters.Use a UUID or another 16–128 character key without spaces at either end.
401invalid_tokenThe bearer token is missing, malformed, expired, revoked or issued for a different resource (for example an MCP sign-in).Send a current REST API token or REST OAuth access token as Authorization: Bearer ….
403api_access_not_enabledFieldTask hasn't enabled API access for this organization, or has disabled it.An owner or admin requests access in Settings → API connections.
403insufficient_scopeThe connection lacks one of the permissions this request needs. error.code is missing_permission.Add the permission named in error.message to the connection, then retry with the same key.
403operation_forbiddenThe connection's access changed while the request was processed, or a replay would return a customer the connection can no longer read.Check the connection's permissions, then retry with the same key and body.
404resource_not_foundInvoicing isn't enabled for your organization, invoices aren't available in this environment, or the URL is wrong.Check that your organization can use invoices in FieldTask, and that the path has no trailing slash.
405method_not_allowedInvoice creation isn't enabled in this environment. The Allow header lists the methods that are.Contact FieldTask if you expected invoice creation to be available.
409idempotency_key_reusedThis key was already used with a different body.Resend the original body with this key, or use a new key for a different record.
409operation_in_progressA request with the same key is still being processed.Wait for Retry-After seconds, then retry with the same key and body.
409idempotency_window_expiredThis key was used more than seven days ago. It can't be replayed or reused. This doesn't mean the original request failed.Reconcile using your saved invoice ID, or list the customer's draft invoices, before creating anything new.
410resource_goneA replay of this key found that the invoice it created has since been deleted.Don't create it again automatically. Check in FieldTask whether it was removed on purpose.
413payload_too_largeThe body is larger than 256 KiB.Reduce the body, for example by sending fewer line items.
415unsupported_media_typeContent-Type isn't application/json, or the body is compressed.Send an uncompressed body with Content-Type: application/json.
422validation_failedA field is missing, unknown or invalid; the customer or a product isn't eligible; a price selection matches no option or more than one; an item is flagged with a tax the invoice doesn't define; a percentage discount is over 100; or your plan's invoice allowance for today is used up.Check the request against the field guide, and send price_id for products with several prices. The response doesn't name the field.
429rate_limit_exceededToo many requests in the current minute, or too many concurrent requests, for the organization or connection.Wait for Retry-After seconds. Retry creates with the same key.
429daily_quota_exceededThe organization or connection has used its daily API create allowance. Allowances reset at 00:00 UTC.Wait for Retry-After seconds, or check remaining allowance in Settings → API connections.
503temporarily_unavailableThe service couldn't complete the request. The invoice may or may not have been created.Retry with the same key and body, with exponential backoff.

Every response, including errors, has an X-Request-ID header. Include it, with the time and the status code, when you contact support. Never send tokens or secrets.

Limits​

Invoice creates count against the same limits as other creates: per-minute and concurrency limits, and a daily create allowance that resets at 00:00 UTC, for both the organization and each connection. When you hit a limit you get 429 with a Retry-After header in seconds. Replaying an already-created invoice with its original key doesn't count as another create. See Rate limits.

Your FieldTask plan also applies. On the free plan, or without a plan, an organization can create one invoice per day in its own time zone, counting invoices created in FieldTask as well as through the API. A further create that day is rejected with 422 validation_failed.

Response headers​

A successful create returns 201 Created with these headers:

HeaderAlways presentValue
Cache-ControlYesAlways no-store
Idempotency-KeyYesstring
Idempotency-ReplayedYesstring
LocationYesstring
Operation-LocationYesstring
X-Request-IDYesuuid

The server also returns the Idempotency-Key that was used, including one it generated.