Developers / Guides / Records
Customers, products and tax rates
The records every invoice, schedule and saved card hangs off. Everything here
is per merchant, addressed by that merchant's X-Api-Key-scoped
{ref} (its externalReference).
Customers
Create a customer, then use the returned customerId everywhere else. You can also give
a customer your own account number — your system's number for them — and find them
again by it. It is unique per merchant (a duplicate returns 409), it is sent on to any
accounting system the merchant has connected, and it is never required.
| Endpoint | Purpose |
|---|---|
| POST /api/merchant-accounts/{ref}/customers | Create a customer (name, email, phone, website, billing address, optional accountNumber, optional smsConsent). |
| GET .../customers | List the merchant's customers. |
| GET .../customers?accountNumber=A-1001 | Find the customer with that account number (an empty list if there is none). |
| GET .../customers/{customerId} | Get one customer. |
| GET .../customers/{customerId}/sms-consent | Whether the customer has agreed to be texted, at which number, and how it was recorded. |
| PUT .../customers/{customerId}/sms-consent | Record or withdraw the customer's consent to receive texts ({ "consent": true, "phone": "(555) 234-5678" }). |
Saved payment methods hang off a customer — see Saved payment methods.
Text message consent
PayMidWare can text a merchant's customers a pay link, a receipt, a failed-payment notice or a reminder before a scheduled payment. It only does so for a customer who has agreed to be texted, at the exact US mobile number they agreed to, and only when the merchant has turned that kind of text on in the Portal (everything is off by default). Texting is a regulated activity, so please read the rules before you record consent.
- Record only consent the customer actually gave — a clear opt-in on your own form or screen. By
sending
"consent": trueyou confirm that the customer gave it. Pre-ticked boxes, or consent given for something else, do not count. - Consent is for one number. If the customer's phone number changes, consent has to be recorded again.
Omit
phoneto use the number already stored on the customer; send it to record consent for another number. - You can set it when you create the customer (
"smsConsent": truewith a valid phone, else400) or later withPUT .../sms-consent."consent": falsewithdraws it. - STOP always wins. A customer who replies STOP to any text is blocked for that number until they reply START, and their consent is withdrawn. Replying START does not give consent back — the customer has to agree again.
- Numbers outside the US, and numbers that are not a valid mobile format, are refused (
400). Texts are not sent between 9pm and 8am in the merchant's time zone; they are held until morning. - The customer can also agree on the pay page after a successful payment, and a merchant's staff can record it on the customer's page in the Portal.
PUT /api/merchant-accounts/{ref}/customers/{customerId}/sms-consent
{ "consent": true, "phone": "(555) 234-5678" }
200 { "consent": true, "phone": "+15552345678", "source": "Api", "recordedAtUtc": "2026-10-08T14:03:11Z" }
Products
A catalog of what the merchant sells. A product has a name, optional SKU (unique per merchant),
description, unit price, a kind (Service, NonInventory or
Inventory) and a taxable flag. PayMidWare never tracks stock — the kind only decides which
kind of item is created or linked in QuickBooks. Put a product on an invoice or scheduled-payment line
with productId; the line still carries its own description, price and taxable flag, so
editing a product later never rewrites an invoice that already exists.
| Endpoint | Purpose |
|---|---|
| GET .../products | List products. |
| GET .../products/{productId} | Get one, including whether it is linked to a QuickBooks item. |
| POST .../products | Create a product. |
| PUT .../products/{productId} | Edit a product. |
| POST .../products/{productId}/deactivate | Take it out of use (also /activate). Products are never deleted. |
| PUT .../products/{productId}/category | Set the product's category with categoryId, or clear it with null. |
Product categories
Flat groups such as Drinks or Food. A product is in one category at a time (categoryId on the
product; send it when creating or editing, leave it out on an edit to keep the current one). The register shows
the active categories as colored buttons above the products, and GET .../products accepts a
categoryId filter. A name is at most 100 characters and cannot contain a colon; a bad name returns 400
and a duplicate returns 409. Color is #RRGGBB. Categories are never deleted, only deactivated. With
QuickBooks Online they sync as QuickBooks categories when the company has item categories turned on.
| Endpoint | Purpose |
|---|---|
| GET .../product-categories | List categories (includeInactive=true to include switched-off ones). |
| GET .../product-categories/{categoryId} | Get one. |
| POST .../product-categories | Create a category (name, optional color). |
| PUT .../product-categories/{categoryId} | Rename, recolor or reorder (sortOrder). |
| POST .../product-categories/{categoryId}/deactivate | Hide it from the register (also /activate). |
Tax rates
Named rates such as "NY State 4%" (a percentage from 0 to 100, up to four decimals). One rate can be
the merchant's default. Send taxRateId when creating an invoice (or an itemized schedule)
and that rate is used; otherwise send a typed taxRatePercent. A rate's percentage is copied
onto the invoice when it is created, so changing a rate later does not change existing invoices.
| Endpoint | Purpose |
|---|---|
| GET .../tax-rates | List rates. |
| GET .../tax-rates/suggestion?postalCode= | QuickBooks automatic sales tax: the combined rate QuickBooks was last seen charging for that ship-to postal code (found, percent, its jurisdictions, lastConfirmedAtUtc, isStale after 30 days). A suggestion only; send it as taxRatePercent on the invoice if you want it. |
| GET .../tax-rates/observed | The newest observed rate for each postal code, and whether the merchant's QuickBooks Online company has automatic sales tax on. |
| POST .../tax-rates | Create a rate. |
| PUT .../tax-rates/{taxRateId} | Edit a rate. |
| POST .../tax-rates/{taxRateId}/default | Make it the merchant's default. |
| POST .../tax-rates/{taxRateId}/deactivate | Take it out of use (also /activate). |
Tax is worked out per line: only lines
with isTaxable: true (the default) are taxed, at the invoice's rate, and the tax is rounded
per line. Merchants connected to QuickBooks can also import their QuickBooks tax rates and sync their
products from the Portal.
Locations
A merchant's stores or sites. A location has a name, an address, a phone and, optionally, its
own tax rate (taxRateId, one of the merchant's named rates). They are the same
locations the point-of-sale registers belong to. A register sale uses the rate of the register's location,
and an invoice created with a locationId starts on it; a location with no rate of its own (or
whose rate is switched off) uses the merchant's default. Locations are never deleted: set
isActive to false to switch one off (refused while it has active registers, or when it is the
last active location).
| Endpoint | Purpose |
|---|---|
| GET .../locations | List locations (includeInactive=true to include switched-off ones). |
| GET .../locations/{locationId} | Get one. |
| POST .../locations | Create a location (name, optional taxRateId, phone, address fields). |
| PUT .../locations/{locationId} | Replace its name, isActive, tax rate, phone and address (anything left out is cleared). |
| GET .../locations/settings | Whether the merchant uses locations (locationsEnabled) and its defaultLocationId. |
| PUT .../locations/settings | Replace both settings. The default must be one of the merchant's active locations. |
| PUT .../locations/{locationId}/default | Make this location the default. |
| DELETE .../locations/default | Clear the default. |
| PUT .../customers/{customerId}/location | Set (or, with locationId: null, clear) the customer's usual location. |
On POST .../invoices, send
locationId to raise the invoice for a location. When neither taxRateId nor a
non-zero taxRatePercent is sent, the location's own rate is used; an explicit rate or
percentage always wins. The location is recorded on the invoice (locationId).
Default location. When the merchant has
locations switched on, a new invoice, charge or customer-linked record with no locationId gets the
customer's location, else the merchant's default. A locationId you send always wins and must be the
merchant's own (otherwise 400). This applies to new records only; changing a default or a customer's location
never changes an existing invoice. Charges accept locationId too, and the transaction records it
(it is not sent to the gateway); captures, refunds and voids keep the original charge's location.
POST .../customers takes an optional locationId.
Location codes. A location can carry a short
code (1 to 6 letters or digits, stored in capitals, unique per merchant; send it on
POST and PUT .../locations, left out clears it). Merchants can place it in their invoice
numbers with {LOC}, for example {LOC}-{YY}-{#####} gives NYC-26-00042. An invoice with no
location, or whose location has no code, leaves it out.