Skip to content

Update an invoice

Open .md

PUT/v2/api/store/{team_slug}/v2/invoice/{unique_key}

v2Operation ID v2_public_invoice_update

Changes an invoice's header (number, issue date, addresses) and, in the same save, its lines, its tax and the order of its lines. Everything in one call is saved together or not at all.

  • item_actions adds, changes and removes lines: each entry is like a row of Add, change and remove invoice lines at once (deletes run first, then creates, then updates). Tax lines can't be added or changed here; a delete of the tax line removes it (except on Auto Invoice invoices).
  • tax_selection sets, changes or removes the one tax line, as in Change the tax on an invoice.
  • resort_items: true spaces out the line positions again, keeping their order.

Returns the invoice; its items are included when you sent item_actions, tax_selection or resort_items.

Address changes and tax: updating only the header addresses does not automatically recalculate tax on a manual invoice. To update tax for a new address on a manual invoice, include tax_selection in your request. When the order has Auto Invoice turned on, YoPrint automatically maintains a single invoice (auto_invoice_managed: true): whenever you add, edit, or remove lines or shipping fees on the order, YoPrint automatically updates the matching lines, shipping fees, and tax on this invoice. On an Auto Invoice invoice, changing the destination address automatically recalculates the managed Auto Tax line (custom tax lines you set manually are left unchanged).

Permission

Needs permission to edit the invoice's sales order: the team owner, a role whose Manage Sales Orders permission is "update all", or "update assigned" when you created the order or are assigned to it, or, on a draft order, its creator when their role can create sales orders. Otherwise 403 user_is_not_authorized. Reading needs only team membership.

Business rules

  • The invoice's customer is always reset to the order's customer.
  • On an Auto Invoice invoice, a new Bill To or Ship To must be a current address of your team or the customer ("The selected billing address must be a current address."), and resort_items is refused.
  • On an invoice from Shopify, the address ids can't change: 422 shopify_invoice_snapshot_update_boundary.
  • On an order deleted in Shopify nothing can change: 422 shopify_order_deleted_externally.
  • Tax lines can't be added or changed in item_actions: "Tax Item is not allow in bulk operations." A delete of the tax line there removes it on an invoice without Auto Invoice (refused on one with it). Use tax_selection for tax changes.
  • Every rule of the line calls applies to item_actions rows.
  • In tax_selection, both expected_tax and desired_tax must be present; null is allowed for either. Missing: 422 "The tax_selection.expected_tax field must be present." (or desired_tax).
  • expected_tax must match the current tax line exactly (id and lock_version), or be null when there is none. Otherwise 422 invoice_tax_conflict: "Invoice tax changed in another window. Reload and retry."
  • A different type from the current one replaces the line (new id); the same type updates it; null deletes it.
  • A tax line you set is yours: YoPrint no longer manages it, even on an Auto Invoice invoice.
  • Auto Tax needs a US plan with Auto Tax turned on: otherwise 422 cart_item_validation_error with error.context.reason auto_tax_unavailable.
  • Synced (external) invoices are read-only here: 422 external_invoice_tax_snapshot_read_only.

Side effects

  • Line and tax changes recalculate the invoice and the order's invoiced totals, and automatically redistribute (reallocate) the order's payments across its invoices.
  • When the team syncs to QuickBooks Online automatically and the order is published, the order is synced again about 3 minutes later.
  • Auto Tax (on create, change, or an address change on an Auto Invoice invoice) calls the tax provider while you wait.
  • Open Admin Portal pages refresh, and so do Customer Portal pages the order's customer has open. No webhook or email is sent.

Path parameters

  • team_slugstringrequired

    Your team slug, shown as Team ID next to your API key in Profile.

    • Example your-team
  • unique_keystringrequired

    The invoice's id (a hash id), as its answers show it. Its unique key (ordinv_...) also works, but API answers don't include it.

    • Example cBRpSf5nw6upt5

Request body

application/json, required.

  • billing_address_idstring | null

    Bill To address hash id. Null clears it.

  • issue_datestring (date-time)

    The issue date (ISO 8601).

  • item_actionsarray of objects

    Line changes, applied in the same save. Tax lines can't be created or updated here (use tax_selection).

    Fields of each item (4)
    • actionstringrequired
      • One of: "update", "create", "delete"
    • bidstringrequired

      Your label for this row, echoed in errors.

    • The invoice line. Required for update and delete.

    • payloadobject

      Required for create and update: the line body, as in Add a line to an invoice or Update an invoice line.

  • true re-spaces line positions, keeping the current order. Not allowed on Auto Invoice invoices.

  • scoped_idstring

    The invoice number, as a string. Not checked for duplicates.

  • shipping_address_idstring | null

    Ship To address hash id. Null clears it.

  • Set, change or remove the invoice's one tax line. Send both keys; either may be null.

    Child fields (2)
    • desired_taxobject | nullrequired

      The tax you want, or null to remove the tax line.

      Child fields (2)
      • The fields of that kind, as for an invoice line of that product.type: auto_tax takes engine us, from, to and rate; user_tax takes the preset id; override_tax takes mode and total.

      • typestring

        auto_tax (US Auto Tax), user_tax (one of your tax presets) or override_tax (set by hand).

        • One of: "auto_tax", "user_tax", "override_tax"
    • expected_taxobject | nullrequired

      The tax line you think is there now: its id and lock_version from the invoice's items, or null when it has none. If it doesn't match, nothing changes (422 invoice_tax_conflict).

      Child fields (2)
      • idstring

        The current tax line.

      • That line's lock_version.

        • Minimum 0

Response

200Successful response.

  • dataobjectrequired

    An invoice. Amounts are decimal pairs at the stored scale (4 decimal places for invoice totals), except paid_amount and calc_deposit_total (thousandths). The calc_* totals are deprecated copies of the decimal-pair ones.

    Child fields (43)
    • YoPrint keeps this invoice in step with the order (Auto Invoice).

    • How Bill To compares with the order's.

      • One of: "okay", "empty", "diff_from_order", "diff_from_customer"
    • billing_addressobject | null

      An address.

      Child fields (10)
    • billing_address_idstring | null

      Bill To.

    • calc_deposit_totalinteger | number | string | null

      The order's deposit amount. Thousandths, an older encoding: 12500 is 12.50. Usually a whole number; an amount finer than a thousandth comes back as a number with a fraction (1234.5 is 1.2345), or as a string ("1234.5") when you send X-Yoprint-Decimal-Format: exact-v1. See Money and quantities.

    • cart_item_subtotaldecimal pair

      Billed sales order lines and shipping fees (price × quantity); shipment_subtotal is the shipping part of it. total = cart_item_subtotal − discount_subtotal + tax_subtotal (+ credit_card_surcharge). At the stored scale (4 decimal places). A decimal pair: the amount is value ÷ 10^dp ({ "value": 1250, "dp": 2 } is 12.50). Read dp every time: it can differ between fields and records.

    • created_atstring (date-time)
    • A card surcharge added by a card payment; zero when there is none. Always sent. A decimal pair: the amount is value ÷ 10^dp ({ "value": 1250, "dp": 2 } is 12.50). Read dp every time: it can differ between fields and records.

    • customerobject | null

      The customer (the full customer record).

      Child fields (2)
      • idstring

        The customer.

      • namestring
    • deposit_duestring (date-time) | null

      From the order. ISO 8601 with offset, or null.

    • deposit_requiredboolean | integer | null

      From the order.

    • discount_subtotaldecimal pair

      Discounts. A decimal pair: the amount is value ÷ 10^dp ({ "value": 1250, "dp": 2 } is 12.50). Read dp every time: it can differ between fields and records.

    • external_typestring | null
    • full_amount_duestring (date-time) | null

      From the order. ISO 8601 with offset, or null.

    • idstring

      The invoice.

    • is_paidboolean

      Paid amount covers the total (also true for a zero total).

    • is_validboolean

      False when the invoice needs fixing: no lines, lines without a tax line, or a customer that differs from the order's.

    • issue_datestring (date-time) | null

      Issue date. ISO 8601 with offset, or null.

    • itemsarray of objects

      The lines. Sort by position.

      Fields of each item (15)
      • _metaobject

        Read-only: id, type, invoice_id, lock_version, from_legacy, position_hash.

      • YoPrint manages this line (Auto Invoice).

      • external_idsarray of objects

        Links to external systems; empty for lines made in YoPrint.

      • external_typestring | null
      • idstring

        The invoice line.

      • The invoice.

      • Send it back on Update an invoice line to detect a concurrent change.

      • positioninteger
      • pricingobject

        Absent on tax and discount lines. system (sales order lines): _meta.unit_price is the price copied from the sales order line and _meta.subtotal is that price × this line's quantity. fee (shipping fees): product.total is the fee. Prices come at the stored scale (6 decimal places); _meta.subtotal at the combined scale of price × quantity (compacted when large). Always read dp.

        Child fields (1)
        • typestring
          • One of: "system", "fee"
      • productobject
        Child fields (3)
        • _metaobject

          Read-only details, always an object (with editable_fields_on_update); for order_item, parent describes the sales order line.

        • The kind's fields; for order_item, id is the sales order line. A discount's percentage or amount comes back as total (you send it as rate).

        • typestring
          • One of: "order_item", "shipment_fee", "invoice_discount", "user_tax", "override_tax", "auto_tax", "invoice_surcharge"
      • quantityobject

        Absent on tax and discount lines.

        Child fields (2)
        • typestring
          • Always "unit"
        • totaldecimal pair

          The quantity. A decimal pair: the amount is value ÷ 10^dp ({ "value": 1250, "dp": 2 } is 12.50). Read dp every time: it can differ between fields and records.

      • taxobject

        Absent on tax and discount lines. YoPrint adds it by default only to sales order lines (taxable); other lines have it only when it was sent.

        Child fields (1)
        • typestring
          • One of: "taxable", "non_taxable"
      • typestring

        Same as product.type.

    • orderobject

      The sales order or quote in the v2 order shape: its header fields, with status and assigned_users, but no items.

      Child fields (2)
    • order_idstring

      The sales order or quote it bills.

    • order_object_typestring | null

      sales_order or quote.

    • The order is a quote.

    • order_scoped_idstring | null

      The order number.

    • paid_amountinteger | number | string | null

      Allocated payments. Thousandths, an older encoding: 12500 is 12.50. Usually a whole number; an amount finer than a thousandth comes back as a number with a fraction (1234.5 is 1.2345), or as a string ("1234.5") when you send X-Yoprint-Decimal-Format: exact-v1. See Money and quantities.

    • pdf_urlarray of objects

      PDF links.

      Fields of each item (3)
      • labelstring
      • urlstring

        A download link with its own long-lived access token: don't publish it.

    • scoped_idstring | null

      The invoice number.

      • One of: "okay", "empty", "diff_from_order", "diff_from_customer"
    • shipment_subtotaldecimal pair

      Shipping fees (already in cart_item_subtotal). A decimal pair: the amount is value ÷ 10^dp ({ "value": 1250, "dp": 2 } is 12.50). Read dp every time: it can differ between fields and records.

    • The tax on shipping (included in tax_subtotal). A decimal pair: the amount is value ÷ 10^dp ({ "value": 1250, "dp": 2 } is 12.50). Read dp every time: it can differ between fields and records.

    • shipping_addressobject | null

      An address.

    • shipping_address_idstring | null

      Ship To.

    • tax_subtotaldecimal pair

      Tax. A decimal pair: the amount is value ÷ 10^dp ({ "value": 1250, "dp": 2 } is 12.50). Read dp every time: it can differ between fields and records.

    • totaldecimal pair

      The invoice total. A decimal pair: the amount is value ÷ 10^dp ({ "value": 1250, "dp": 2 } is 12.50). Read dp every time: it can differ between fields and records.

    • versioninteger
    • calc_delivery_totalinteger | number | string | nulldeprecated

      Deprecated: use shipment_subtotal, the same amount. Thousandths, an older encoding: 12500 is 12.50. Usually a whole number; an amount finer than a thousandth comes back as a number with a fraction (1234.5 is 1.2345), or as a string ("1234.5") when you send X-Yoprint-Decimal-Format: exact-v1. See Money and quantities.

    • calc_discountinteger | number | string | nulldeprecated

      Deprecated: use discount_subtotal, the same amount. Thousandths, an older encoding: 12500 is 12.50. Usually a whole number; an amount finer than a thousandth comes back as a number with a fraction (1234.5 is 1.2345), or as a string ("1234.5") when you send X-Yoprint-Decimal-Format: exact-v1. See Money and quantities.

    • calc_subtotalinteger | number | string | nulldeprecated

      Deprecated: use cart_item_subtotal, the same amount. Thousandths, an older encoding: 12500 is 12.50. Usually a whole number; an amount finer than a thousandth comes back as a number with a fraction (1234.5 is 1.2345), or as a string ("1234.5") when you send X-Yoprint-Decimal-Format: exact-v1. See Money and quantities.

    • calc_taxinteger | number | string | nulldeprecated

      Deprecated: use tax_subtotal, the same amount. Thousandths, an older encoding: 12500 is 12.50. Usually a whole number; an amount finer than a thousandth comes back as a number with a fraction (1234.5 is 1.2345), or as a string ("1234.5") when you send X-Yoprint-Decimal-Format: exact-v1. See Money and quantities.

    • calc_totalinteger | number | string | nulldeprecated

      Deprecated: use total, the same amount. Thousandths, an older encoding: 12500 is 12.50. Usually a whole number; an amount finer than a thousandth comes back as a number with a fraction (1234.5 is 1.2345), or as a string ("1234.5") when you send X-Yoprint-Decimal-Format: exact-v1. See Money and quantities.

  • statusintegerrequired
  • successbooleanrequired

Errors

  • 401The API key is missing or wrong (unauthenticated).

  • 403You are not allowed to do this. api_access_not_in_plan: your team's plan doesn't include API access (Pro and above, with an active subscription or trial), so every request answers this until the team upgrades. When your role lacks the permission the operation needs, the body is only {"message": "Insufficient Permission"}. Other refusals use the error envelope, with unauthorized or user_is_not_authorized.

    When
    Your role can't edit this order (user_is_not_authorized).
    Fix
    Ask the team owner to adjust your role or assign you to the order.
  • 404Not found. page_not_found: the team slug is unknown, you are not a member of the team, or an id in the path is not one of this team's. code: null, with a message that starts "No query results for": there is no record with that id.

    When
    The invoice isn't your team's.
    Fix
    Check the id.
  • 422The request was refused and changed nothing. validation_failed lists what to fix in error.fields; other codes name a business rule, explained in error.message. A few checks answer with only {"message": "..."}. An operation that calls another service (a payment provider, a carrier, an accounting app) can answer connect_timeout ("Please retry again") when that service does not answer in time: the change may or may not have happened there, so read the record back before you retry.

    When
    Validation fails (validation_failed, with field messages in error.fields such as "The issue date must be a valid ISO8601 string." or "The scoped id must be a string.").
    Fix
    Fix the fields in error.fields.
    When
    A line row or the tax selection is refused (see rules); nothing in the call is saved.
    Fix
    Read error.code and error.message, fix that part and resend the whole call.

Notes

  • Send the header X-Yoprint-Decimal-Format: exact-v1. Without it, a save that would return an amount the old number format can't carry is refused with 422 amount_out_of_range.
  • There is no locked or permanently paid state: you can change a paid invoice, and YoPrint automatically redistributes (reallocates) the order's recorded payments across its invoices.