Skip to main content
An item is a name for something you sell — “Neural Network Training - Standard Seat,” for example. You tag your billable metrics and fees with an item so that every invoice line they produce records what was sold, not only what was charged. If you also keep that SKU in an accounting, CRM, or tax system, external references link the two, and one API read then translates a whole invoice into the codes that system expects. That mapping is a separate layer; it doesn’t change what the item records on the line.

What’s an item?

Items are optional. Nothing requires one: a billable metric or fee filed directly into a product bills exactly as it always has. Paygentic doesn’t create items for you — you create them when you want your invoice lines to name what they sold. Mapping those names to an external system is a further step. Items are also deliberately thin. An item has:
  • A name - the canonical, customer-facing SKU name
  • A catalog ID - the product it belongs to, if you’ve filed it into one
  • Metadata - optional key-value pairs for internal tracking
  • External references - links to your CRM, ERP, or tax provider
An item never carries a price, currency, plan, or metering configuration. Those live on billable metrics and fees, which tag an item through itemId.

Creating an item

catalogId is optional at creation time — you can file an item into a product later by updating it, and you can move a filed item to a different product at any time. Re-filing carries the item’s tagged billable metrics and fees to the new product with it, in one transaction. You cannot, however, un-file an item (set catalogId back to null) while billable metrics or fees are still tagged with it — untag them first.

Tagging metrics and fees with an item

Billable metrics and fees accept an itemId that points at the item they bill for:
Supply either productId or itemId — at least one is required. Set itemId and the product is resolved from the item’s catalogId for you; set productId and the metric or fee is filed directly, with no item tag. If you pass both, they must resolve to the same product. productId is always populated on the response either way.

From the dashboard

Everything below can be done without writing code.
1

Create your items

Open a product and go to its Items tab. Items you create there are filed under that product, which is what makes them taggable — an item belonging to no product cannot be attached to a charge.
2

Tag your charges

Each billable metric and fee has an optional Item picker on its form. It offers only the live items of that charge’s own product, because those are the only ones the API will accept.
3

Map the item to your codes

Click an item’s name to open it. External mappings lists one row per integration you have connected — you cannot name a system you have not connected, because a code for one would be read by nothing. Type a code and save; add more than one if the system genuinely uses more than one for that item.Nothing is fetched from your accounting system; you supply the codes. Each row says what its system expects, because the same field means different things: a NetSuite item internal ID is not a ledger account code, and entering one for the other saves cleanly and books revenue against an unrelated record.
4

Fix invoices that were already issued

On the Items tab, “N past invoice lines are still untagged” counts lines that were generated before their charge was tagged. Apply to past invoices fills them in. Read the confirmation before accepting it: amounts never change and an existing tag is never overwritten, but a re-export of an already-issued invoice will carry a code where it previously carried none.
Which way the codes travel decides how many there can be.Where a system sends you codes — a CRM’s product codes arriving on an order — several of them may bill as one item, and each one resolves to that item alone. So a second item cannot take a code the first already resolves from; if you try, you are told which item holds it and offered to move it, which changes what future orders bill as and leaves issued invoices as they are.Where a system is sent a code — a ledger account — several items may share it. That is the point of a chart of accounts: a dozen SKUs posting to 4000 Revenue is normal, not a conflict. An item may hold more than one code for such a system, as happens through the system’s own migration, and exactly one of them is marked in use — the code its revenue actually goes out under. Use this moves that marking to another of the item’s codes.You do not choose between these. Each integration declares what it does with codes, and the surface follows it.

Map items to your accounting system

Tagging a charge with an item records what was sold on every invoice line it produces. Mapping that item to an external code records what your accounting system calls it. Together they let one API read translate an entire invoice into GL or SKU codes.

1. Record the external code

External codes live on the item as external references, not as fields on the item itself. One item can carry codes for several providers at once:

2. Read invoice lines with their codes resolved

Pass expand=items to any read that returns invoice lines. The response gains an items collection; each line joins to it through its itemId:
The expansion is available on GET /v2/invoices/{id}/lineItems, GET /v2/invoices/lineItems, and GET /v2/invoices/{id} (where asking for items expands lineItems too, since the item IDs come from the lines). Three things to know about the collection:
  • Each distinct item appears once, ordered by ID. Join through itemId.
  • It describes the lines in that response, not the whole invoice. If you page through a large invoice, combine the collections across pages. On GET /v2/invoices/{id}, asking for items without naming lineItems pages the lines at the default size (100), so a larger invoice returns a partial collection and a nextPageToken inside the lineItems block — read it.
  • provider narrows which references come back, never which lines or items. An item with no code for that provider is returned with an empty list, so an unmapped SKU stays visible instead of vanishing from your reconciliation. A provider that isn’t lowercase snake case is rejected with a 400 rather than answered with an empty list that would read as “nothing is mapped”.
  • An item can carry more than one reference for the same provider, and more than one of those can be marked isPrimary. The uniqueness rule runs the other way round — an external code is the primary reference of at most one item — so it does not reduce an item to one code per provider. Order is stable across reads, but choose deliberately rather than taking the first.

3. What a line tells you

The response answers two independent questions, and it helps to read them in order. The first applies to everyone. The second only matters if you’re syncing to an external ledger. What was sold? This comes from the item alone and needs no external codes. priceId is what separates a charge nobody tagged from a line that never had a charge behind it, and the distinction matters: only the first can be fixed. What does your ledger call it? Read the resolved item’s externalReferences. This layer only applies if you’re mapping to an external system. If you’re tracking how much mapping work is left, count lines whose item has no reference for your provider, and exclude lines with a null priceId — those can never be tagged, so a count that includes them never reaches zero.
One combination belongs to neither list: itemId set with no entry in items. The tag points at an item that no longer resolves for you, and the item can’t be fetched from /v0/items either. This is a data-integrity fault, not an unmapped line — don’t count it and don’t try to map it. Raise it.
Re-tagging a charge takes effect on everything that hasn’t been billed yet — including the period currently running. You don’t have to do anything else, and there’s no window to wait for.Lines are generated ahead of time, so at any moment your next bill or two already exists. Those lines don’t record an item of their own: they report whatever their charge is tagged with, right up until their invoice closes. So a re-tag changes what all of them say at once.Invoices that have closed never move. The item is written onto the line at close, and from then on it’s a fact about that period — a later re-tag cannot change what a closed month exported under. If a line closed while its charge carried no tag, tagging the charge afterwards won’t fill it in either; that is what re-applying a tag to past invoices is for, and it only ever fills blanks.One exception: manual line items. A manual line has no charge behind it — you gave it an item when you created it — so there’s nothing for a re-tag to reach. It keeps the item you set, on every bill, and changing it means editing the line.

Filling past invoice lines over the API

Two operations, deliberately separate so that reading the count can never modify invoice data.
These two are internal to the Paygentic dashboard and are not part of the public API. Use Apply to past invoices on the product’s Items tab instead.
lines counts only what a fill would touch: the line has a charge behind it, that charge is already tagged, and the line records no item yet. Lines with no charge behind them are never counted, so the figure can actually reach zero. issuedLines and issuedInvoices are the ones to show a user before asking them to confirm — an invoice counts as issued once it has been issued at all, whatever its status is now, because it may already have been exported.
This is the one operation in the chain that modifies already-issued invoices. It fills absent tags only — a line that already records an item is never changed, and no monetary field moves. PATCH rather than POST because it is convergent: a second call fills only what the first did not, so repeating it settles. Show filled, not the figure you previewed: the gap can move between the two calls. remaining is measured after the write in the same request, so a partial fill reports a smaller remainder rather than reading as complete.

One canonical path

A line’s external identity is always resolved line → item → references. Codes are never stamped onto lines directly. Keep it that way: a future connector should read through this path rather than record its own copy, or the same line ends up with two answers.

What this doesn’t cover

Refunds aren’t attributed per item. A refund creates an invoice-level credit note with no line breakdown, so the credit can’t be allocated to a product. Net-revenue-by-item is accurate until an invoice is refunded. There is no date filter on invoice reads. Each read resolves its lines to codes in one pass, but neither GET /v2/invoices nor GET /v2/invoices/lineItems filters by period. A monthly export pages the invoice list, filters client-side, and pulls lines per invoice.

Archiving items

Archiving retires an item from your catalog so it can no longer be attached to new pricing, while keeping it readable so historical invoices and line items still resolve to it. Archiving is the only way to retire an item — there is no delete operation, because a deleted item would leave the invoices that already reference it pointing at something you can no longer read.

Archiving frees the item’s external code

An external code has one canonical owner per provider. Archiving an item releases its claim, so a replacement item can take the code over:
The archived item keeps the code as a non-primary association rather than losing it, so its own invoices continue to export under it. Restoring the item does not give the code back — by then another item may own it. Reattach it explicitly if you need to.

Move the charges off the item first

Because archiving hands the code to a replacement, you have to move the charges before you archive — not after. An item with a live billable metric or fee still tagged to it cannot be archived: the request returns 400 with ITEM_HAS_ANCHORED_ROWS, and nothing changes. That ordering exists because line generation resolves a line’s item through its charge. If the item could be archived with a charge still tagged to it, new lines would keep landing on the retired item while the replacement owned the code and received none of them. The check counts charges — billable metrics and fees — because those are the only way an item reaches a generated line. A manual line tagged with the item does not block archiving, and keeps the item you gave it, so a manual line you’ve already created can still bill under the retired item. Edit the line if that’s not what you want. So a handover runs in this order:
Step 4 is a POST, not a PATCH: nothing before it created a reference for v2, so there is no row to update. PATCH /v0/externalReferences/{id} moves an existing reference’s flags and takes no entityId, so it cannot be used to attach a code to a different item.
Archiving blocks new attachments. It does not stop existing ones.A charge that is already tagged with an item keeps billing, and keeps recording that item on every invoice line it generates. Applying tags to past invoices does the same. Only a new attachment is refused — tagging another charge with it, or adding it to a manual line item, returns ITEM_ARCHIVED.So archiving an item is not a way to stop it appearing on invoices. Retire the charges that use it for that. The rule is one sentence: archived refuses new attachment decisions and never blocks a replay of an existing one. The dashboard tells you how many charges are still attached before you confirm.
To archive an item, set archived: true on an update:
The response reflects the retirement timestamp:
To restore an archived item, set archived: false. By default, a GET /v0/items list omits archived items. Pass includeArchived=true to include them:
Resolution queries are the exception: a lookup by provider and externalId always returns its match, archived or not, so an external code never silently resolves to nothing.