When to use this endpoint

Use when a job needs its first invoice. This creates an empty draft header, not a priced copy of job stock, labour or quote rows; populate it through invoice editing afterwards.

Repeated calls reuse an active direct/legacy invoice, selecting the lowest invoice ID if legacy data contains several. Creation is serialized on the active job; it does not add a duplicate invoice for an existing match.

Authentication and permissions

Requires signed API-key or CRM-session authentication and an active job in that tenant. crm_access bit 64 blocks invoice creation and reuse before any writes.

Dispatcher user/account/IP restrictions and session CSRF apply. Finance restrictions are checked before the transaction, not only by the final invoice read.

The dispatcher also applies user/account and IP restrictions. CRM sessions carrying a CSRF token must supply X-FMCSRF or _csrf; missing or incorrect tokens can be rejected before the handler runs.

Parameters

ParameterTypeRequiredDescription
idInteger stringYesPositive active physical job ID.

Example request

$result = FM_api('Job_invoiceGenerate', [
	'id' => '1042',
]);

Example response

{"id":"501","total":"0","paid":"0","rows":"[]","notes":"","num":"INV-501","type":"0","status":"-1","discount":"0","pandp":"0","discount_vat":"0","pandp_vat":"0","total_net":"0.0000","published":null}

Representative core fields from the full Invoice_get response; additional date/customer/form/tax/metadata fields are returned. Database scalar values remain strings and rows is an encoded JSON string, not a native array.

Returns Invoice_get for the exact reused or newly created invoice ID after committing. Existing legacy duplicates are not deleted by this call.

Errors and unusual responses

ResponseCondition
{"error":"not logged in"}No authenticated tenant context is available. Invalid signed credentials can be rejected by the dispatcher before the handler runs.
{"error":"missing or invalid parameter: id"}Missing/nonpositive id.
{"error":"Job 1042 does not exist"}The requested job is missing/deleted; message includes the supplied ID.
{"error":"This user does not have access to certain CRM features. Check user permissions."}crm_access bit 64 blocks the call before any writes.
{"error":"Unable to save job delivery changes"}A checked database write, audit or transaction failed; local transactional writes roll back. Cross-tenant files/commits are not distributed-atomic.

Legacy application errors may be returned with HTTP 200. Check the decoded response for error, not only the HTTP status.

Behaviour and edge cases

  • Creates invoice type 0, status -1, paid/total 0, blank notes and rows=[], with current creation time/user. Chooses the first unarchived type-7 form without explicit ordering or a deleted-form filter; no valid-form requirement is enforced here.
  • Invoice numbering uses acc-inv-num-method: 2 leaves num blank; 1 selects the largest nonblank active number by length then lexical order; otherwise it selects the latest-created active invoice number. The legacy increment joins all digits, increments with their combined width and appends them to the remaining nondigits. It is not a locked sequence allocator.
  • Copies a linked quote ID from the first type-2 relation and uses the job customer reference column, then meta fallback, as order_no, truncating to 32 bytes.
  • Nominal-code precedence is recognized customer meta.default_nominal account ID, Job Type nominal_code, then the default account only when autoload-invoice-tax is 1/on/true/yes (case/space normalized). meta.nominalID is stored when that code resolves to an account.
  • The default-account helper prefers a valid default-nominal account ID, then one unique acc-default-nominal code match, then one unique is_default account; ambiguous fallbacks yield no account. This lookup does not persist a repaired setting.
  • Selects a compatible tax category with default-vat rate, then a compatible customer meta.default_taxcode override. Multiple compatible rate matches have no explicit ordering.
  • Due date uses customer default_invoice_due_date / default_invoice_due_date_mode over invoice-due-days / invoice-due-date-mode. Modes are days_after_invoice_date, days_after_invoice_month_end, day_of_invoice_month and day_of_following_month; month-day values clamp to the month’s last day. A blank/missing inherited value leaves due_date unset, while 0 is explicit same-day terms in days mode.
  • For asset jobs the invoice bills the active asset owner as customer type 0 and retains the asset as target_type=1/target_id. Defaults come from that billed owner, not the asset’s numeric ID.
  • Copies a refreshed/historic customer-details snapshot. No list filters, pagination, completion requirement or copied cost lines apply.

Side effects

  • Allocates an invoice number, inserts the invoice and type-3 relation, logs invoice/job history and sets the billed customer’s has-invoices flag. No invoice email is sent.
  • The final Invoice_get can refresh an incomplete invoice customer-details cache. Creation/link write failures roll back the transaction and return an error.

See it in Fieldmotion

CRM1

On the job invoice screen, Generate calls Job_invoiceGenerate; the current Create path can also call Job_invoiceUpdate, which creates an invoice when absent. Filter both and inspect returned invoice fields.

CRM2

No direct Job_invoiceGenerate caller was found. The legacy job invoice/table editor calls Job_invoiceUpdate and can reach generation internally; mapped invoice creation is a separate workflow.

Related endpoints