FieldCamp
Resources

Invoices | FieldCamp API

Use the FieldCamp Invoices API to create, list, update, void, and collect on v1 invoices with line items, discounts, markup, and Stripe payment links.

The FieldCamp Invoices API is how your integration turns completed work into money in the bank — create draft invoices, attach line items with document-level or item-level discounts and markup, push status changes, and hand the customer a hosted Stripe payment link. With release_v2, the resource is available under the versioned path /api/v1/invoices, which adds server-side clientId, status, and since filters and a dedicated payment-link endpoint. If you want the in-app workflow instead, the Creating invoices guide walks through the UI step by step.

What changed in release_v2

The /api/v1/invoices namespace is now the recommended path for new integrations. The unversioned /api/invoices endpoints still exist for backward compatibility, but they do not expose the new filters or the Stripe payment-link helper.

  • Versioned base path: /api/v1/invoices (instead of /api/invoices).
  • Server-side filters on the list endpoint: clientId, status, and since.
  • Status vocabulary aligned with the in-app invoice views and analytics: unpaid, paid, partial, and overdue.
  • Document-level and item-level discount and markup fields on the same payload.
  • New GET /api/v1/invoices/{id}/payment-link endpoint that returns a hosted Stripe checkout URL.

This endpoint requires an fc_live_ API key. See Authentication for header conventions, Errors for the error envelope, and Rate limits for throttling rules.

When to use the Invoices API

Most integrations hit /api/v1/invoices the moment a job is wrapped — the technician marks the job complete, and your backend rolls up the products and services used on the visit into one invoice for the client. You can also use it to:

How an invoice is structured

Every FieldCamp invoice has three layers — header, line items, and totals — and the API mirrors that shape one-for-one.

  • HeaderclientId, billing/property address, invoice number, issue date, due date, currency, notes, optional reference to the originating job, and optional document-level discount and markup.
  • Line items — an array of items pulled from your price book and bundles. Each line carries itemId, description, quantity, unitPrice, an item-level markup (percent or amount), an item-level discount (percent or amount), and taxIds resolved from Taxes.
  • TotalssubTotal, discountTotal, markupTotal, taxTotal, and total are calculated server-side from the line items so totals always match the audit trail.

Reuse the same itemId values you use on jobs. That keeps the client detail page consistent and avoids creating duplicate items in your price book.

GET /api/v1/invoices accepts the following query parameters:

  • clientId — return invoices for a single client. Useful for portals and statement views.
  • status — exact match against unpaid, paid, partial, or overdue. partial covers any invoice with deposits applied but a balance remaining.
  • since — ISO 8601 timestamp. Returns invoices whose updatedAt is greater than or equal to the supplied value. Ideal for nightly delta syncs.
  • limit and cursor — standard pagination parameters used across the API.

Example: invoices for one client

curl "https://api.fieldcamp.ai/api/v1/invoices?clientId=$CLIENT_ID" \
  -H "Authorization: Bearer $FIELDCAMP_API_KEY"

Example: pull overdue invoices

curl "https://api.fieldcamp.ai/api/v1/invoices?status=overdue&limit=100" \
  -H "Authorization: Bearer $FIELDCAMP_API_KEY"

Example: incremental sync

curl "https://api.fieldcamp.ai/api/v1/invoices?since=2026-05-01T00:00:00Z" \
  -H "Authorization: Bearer $FIELDCAMP_API_KEY"

Create an invoice with line items, discounts, and markup

Send a POST /api/v1/invoices request with the header and a lineItems array. Discounts and markup can be applied at the line level on each item, or as a single document-level value that applies to the whole invoice. Markup is computed on unitPrice before tax; discounts are computed after markup.

Look up or create the client

Call GET /api/v1/clients?search=<email> to find an existing record, or POST /api/v1/clients to create one. Reuse the returned id as the clientId on the invoice.

Resolve item and tax IDs

Pull itemId values from Items and taxId values from Taxes. Cache them locally — they almost never change between invoices.

Build the line items

For each line, set quantity, unitPrice, optional markup ({ type: "percent" | "amount", value }), optional discount ({ type: "percent" | "amount", value }), and taxIds. The server multiplies, marks up, discounts, then taxes — in that order.

POST the invoice

Send the payload to POST /api/v1/invoices. The response includes the server-calculated subTotal, discountTotal, markupTotal, taxTotal, and total so you can reconcile before persisting locally.

Store the returned ID

Keep the returned id alongside your internal record. You will need it for status updates, edits, and the Stripe payment-link call below.

Example request

curl -X POST https://api.fieldcamp.ai/api/v1/invoices \
  -H "Authorization: Bearer $FIELDCAMP_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "clientId": "cli_123",
    "jobId": "job_456",
    "issueDate": "2026-05-29",
    "dueDate": "2026-06-12",
    "status": "unpaid",
    "currency": "USD",
    "discount": { "type": "percent", "value": 5 },
    "lineItems": [
      {
        "itemId": "itm_789",
        "description": "Annual HVAC tune-up",
        "quantity": 1,
        "unitPrice": 199.00,
        "markup": { "type": "percent", "value": 10 },
        "taxIds": ["tax_001"]
      }
    ],
    "notes": "Thanks for your business!"
  }'

Send monetary values as numbers in major units of the invoice's currency (e.g. 199.50). Mixing strings and numbers across line items is the most common cause of 400 validation errors. See Errors for the full catalog.

Update invoices and move statuses

  • Fetch oneGET /api/v1/invoices/{id} returns the full invoice including computed totals and any signature blocks captured in the field.
  • UpdatePUT /api/v1/invoices/{id} accepts the same body as create. Send only the fields you want to change; omitted fields are left untouched.
  • Status transitions — set status to unpaid, paid, partial, or overdue. Status drives where the invoice shows up in the invoice views and analytics and which clients see it in their portal.
  • DeleteDELETE /api/v1/invoices/{id} removes a draft. Once an invoice has been sent or partially paid, prefer keeping the record and changing status instead so your reporting stays intact.

overdue is set automatically by FieldCamp when dueDate passes and the invoice is not fully paid. You can also set it explicitly via PUT if your billing rules require it earlier.

If your tenant has connected Stripe, call GET /api/v1/invoices/{id}/payment-link to generate a hosted Stripe checkout URL for the invoice. The response includes the url, the expiresAt timestamp, and the underlying stripePaymentIntentId for reconciliation.

curl https://api.fieldcamp.ai/api/v1/invoices/$INVOICE_ID/payment-link \
  -H "Authorization: Bearer $FIELDCAMP_API_KEY"

You can email the same link to the client via two-way text messaging, or embed it in your own customer portal. Stripe handles the card capture; FieldCamp posts the payment back against the invoice automatically and moves the status from unpaid (or partial) to paid when the balance hits zero. For deposit-style up-front payments, see Deposits and partial payments.

Endpoints

  • GET /api/v1/invoices — list invoices filtered by clientId, status, and/or since.
  • POST /api/v1/invoices — create an invoice with line items, discounts, markup, and tax.
  • GET /api/v1/invoices/{id} — fetch a single invoice including computed totals.
  • PUT /api/v1/invoices/{id} — update header fields, line items, or status.
  • DELETE /api/v1/invoices/{id} — delete a draft (prefer a status change for sent invoices).
  • GET /api/v1/invoices/{id}/payment-link — return a Stripe hosted payment URL for the invoice.

Best practices

FAQs

Which statuses can I send to POST and PUT? unpaid, paid, partial, and overdue. New invoices default to unpaid if no status is sent.

How do document-level and item-level discounts combine? Item-level markup and discount are applied first while computing each line. The document-level discount is then applied to the resulting subTotal before tax. The final total is subTotal - documentDiscount + taxTotal.

Can I filter the list endpoint by both clientId and status? Yes. All three filters (clientId, status, since) are independent and can be combined in any order on GET /api/v1/invoices.

Does the payment link expire? The response includes expiresAt. Generate a fresh link if more than 24 hours have passed since issuance, or whenever the invoice balance changes.

What happens if Stripe is not connected? GET /api/v1/invoices/{id}/payment-link returns a 409 error. Connect Stripe via Connect Stripe for online payments and retry.

Troubleshooting

  • 400 lineItems is requiredPOST /api/v1/invoices requires at least one line item. Add a placeholder labour line if you have nothing else to bill.
  • Totals don't match my upstream system — remember the calculation order: per-line markup, per-line discount, per-line tax, then document-level discount. Compute totals in the same order before comparing.
  • status ignored on update — only unpaid, paid, partial, and overdue are accepted. Other values are silently dropped; check the response body for the canonical status.
  • since not returning new rowssince matches updatedAt, not issueDate. Use the most recent updatedAt you have seen as the next cursor.

On this page