# Create a sales order

`POST /accounting/sales-orders`

Group: Accounting › Sales Orders · Operation ID: `createSalesOrder`

Creates a sales order with its products, subscriptions and pricing in one transaction. Attach to an existing customer with `customerId`, or create one inline with `customer`. Products are grouped as `subscriptions[]` (recurring, each with its own product lines) and `nonSubscriptionProducts[]` (one-time). Line pricing is reconciled from listPrice: supply `unitPrice`, or `discountAmount`, or `discountPercentage` (precedence in that order) and the rest is derived; tiered/volume/stairstep/rateCard lines carry their prices on `tiers`/`rateCardEntries` instead. Rolled back atomically on any validation failure.

[Open in the interactive reference](https://docs.api.lensing.ai/?op=createSalesOrder)

## Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `Api-Version` | header | `"v1"` | yes | The API version this request targets, named by its public identifier. Required on every request; omitting it, or naming a version this company does not have, is answered with 400. This document describes version `v1`. |
| `Idempotency-Key` | header | string | no | An opaque caller-chosen string (1-255 characters) that makes this request safe to retry. Repeating the same key with an identical request body replays the original response instead of re-executing - but only once that original request succeeded; a failed attempt re-executes on retry. Repeating the key with a different body is answered with 422. A key still in flight is answered with 409 - as is a key whose original request was interrupted before it finished, for a few minutes afterwards, until that attempt is presumed dead. A completed response stays replayable for 24 hours. The key is scoped to the caller, not to the action: reusing it for a different action is answered with 409 or 422 rather than treated independently. On read actions this header is ignored. On write actions that cannot offer the guarantee it is answered with `400 IDEMPOTENCY_NOT_SUPPORTED` rather than silently ignored. |

## Request body

Required, `application/json`: createSalesOrderInput.

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `approvalStatus` | string | no | Send `approved` to have the order approved as soon as it is created. The order is always written as `pendingApproval` first and only then transitioned, so every approval side-effect — invoice-plan generation above all — runs against a complete order. Any other value leaves it pending approval. |
| `bookDate` | string (date) | no | Day the order is booked. Defaults to today, and is the date the entity's active invoice policy is resolved for. |
| `currency` | string | yes | ISO 4217 code every price on the order is denominated in — header, lines, tiers and rate-card rows alike. |
| `customer` | InlineCustomer | no | A new customer to create alongside the order. Provide exactly one of `customerId` and `customer`. It is created through the same path as the Customer create endpoint, so anything that path rejects fails the whole request and leaves no order behind. |
| `customer.additionalContactEmail` | string | no | Secondary address copied on customer correspondence. |
| `customer.addresses` | array of CustomerAddress | no | The customer's addresses, each naming the slot it fills. Both a `billTo` and a `soldTo` address are needed before an order can be raised, so supply both here. |
| `customer.addresses[].attention` | string | no | Person or department correspondence to this address is directed to. |
| `customer.addresses[].city` | string | yes | City. |
| `customer.addresses[].country` | string | yes | ISO 3166-1 alpha-2 country code. |
| `customer.addresses[].isDefault` | boolean | no | Marks this entry as the default for its `type`. At most one address per type may set it; when no entry of a type is marked, the first one of that type becomes its default. |
| `customer.addresses[].line1` | string | yes | Street and number. |
| `customer.addresses[].line2` | string | no | Second address line, where the address needs one. |
| `customer.addresses[].stateProvince` | string | no | State, province or region, where the country uses one. |
| `customer.addresses[].type` | string | yes | Which slot this address fills: `billTo` for the address invoices go to, `soldTo` for the contracting party. An order can only be raised once the customer has one of each. |
| `customer.addresses[].zipCode` | string | yes | Postal code. |
| `customer.billableTravelExpenseInvoiceDetail` | string | no | How billable travel expenses appear on the invoice: `detailed` line by line, or `consolidated` into one figure. |
| `customer.businessIdentificationNumber` | string | no | The customer's business registration number. |
| `customer.businessType` | string | no | Whether the customer is a `company` or an `individual`. |
| `customer.customerType` | string | no | A CustomerPartnerResellerType member: `default` or `partner`. `endCustomer` is rejected here, because it requires a partner link this contract does not carry. |
| `customer.defaultCurrency` | string | no | ISO 4217 code the customer is billed in unless an order says otherwise. |
| `customer.entityId` | string | yes | UUID of the legal Entity that owns the customer. Fixed at creation — a customer cannot later be moved to another entity. |
| `customer.exemptionNumber` | string | no | The exemption certificate number. Only accepted while `taxExempt` is true. |
| `customer.externalIdentifiers` | array of ExternalIdentifier | no | Identifiers the customer carries in other systems, upserted by `fieldName`. A `fieldName` may appear only once. |
| `customer.externalIdentifiers[].externalUrl` | string | no | Deep link to the record in the owning system, shown beside the id. |
| `customer.externalIdentifiers[].fieldName` | string | yes | Names the ExternalIdentifierConfiguration this id belongs to, as that configuration spells it. It must already be configured for the tenant; an unknown name is rejected. Identifiers are upserted by this name, so sending it again overwrites the stored value. |
| `customer.externalIdentifiers[].value` | string | yes | The identifier as the owning system spells it. |
| `customer.invoiceConsolidation` | string | no | What invoices are grouped by: `customer`, `purchaseOrderNumber` or `salesArrangement`. |
| `customer.invoiceDeliveryMode` | string | no | Whether invoices go out `automated` or are sent `manual`ly. |
| `customer.invoiceEmail` | string | no | Address invoices are sent to. |
| `customer.isExemptFromCreditCardFee` | boolean | no | Whether credit-card surcharges are waived for this customer. |
| `customer.mainContactName` | string | no | Name of the primary contact person. |
| `customer.mainEmail` | string | no | Primary contact address for the customer. |
| `customer.name` | string | yes | Legal or trading name of the customer. |
| `customer.parentId` | string | no | UUID of the customer above this one in the business-partner hierarchy. |
| `customer.partnerId` | string | no | UUID of the partner this customer buys through. |
| `customer.partnerType` | string | no | How a `partner` customer acts: `reseller` or `thirdPartyPayer`. |
| `customer.payment` | CustomerPayment | no | Payment-provider link registered for the customer once it has been created. |
| `customer.payment.externalCustomerId` | string | no | The customer's id in the provider's system. |
| `customer.payment.externalPaymentMethodId` | string | no | The payment method's id in the provider's system. Requires `externalCustomerId` — a method with no customer to hang it on is rejected. |
| `customer.payment.provider` | string | no | The payment provider, named exactly as the tenant's provider configuration spells it. Required as soon as either external id is supplied, and an unconfigured name is rejected. |
| `customer.paymentTermCode` | string | no | The customer's payment term, named by its code as configured for `entityId`. A code that entity does not have fails the request, and no order is created. |
| `customer.preferredLanguage` | string | no | A PreferredLanguage member; `english` is the only one today. |
| `customer.salesChannel` | string | no | A SalesChannel member: `salesAssisted` or `selfServe`. |
| `customer.segment` | string | no | A CustomerSegment member: `SMB`, `MidMarket` or `Enterprise`. |
| `customer.status` | string | no | Whether the customer is `active` or `inactive`. |
| `customer.taxExempt` | boolean | no | Whether the customer is exempt from tax. Must be true before `exemptionNumber` is accepted. |
| `customer.taxExemptCode` | string | no | The code behind the exemption — the reason category the authority uses. |
| `customer.taxRegistrations` | array of TaxRegistration | no | Tax registrations to record, upserted by (`country`, `type`). |
| `customer.taxRegistrations[].country` | string | yes | ISO 3166-1 alpha-2 code of the jurisdiction the customer is registered in. Together with `type` it is what a registration is upserted by. |
| `customer.taxRegistrations[].effectiveDate` | string (date) | no | Day the registration takes effect. |
| `customer.taxRegistrations[].isActive` | boolean | no | Whether the registration is currently in force. |
| `customer.taxRegistrations[].note` | string | no | Free-text note kept with the registration. |
| `customer.taxRegistrations[].taxNumber` | string | yes | The registration number as the authority issues it. |
| `customer.taxRegistrations[].type` | string | yes | A TaxRegistrationType member: `tax`, `salesTax`, `VAT`, `GST`, `EIN` or `TIN`. Together with `country` it is what a registration is upserted by. |
| `customerId` | string | no | UUID of the existing Customer the order is raised for. Provide exactly one of `customerId` and `customer`; sending both, or neither, is rejected. |
| `dataSource` | string | no | The system this order came from, as an ExtractionProviders member (`Salesforce`, `NetSuite`, `Everest` and the rest). Omit it for an order raised directly through this API. |
| `effectiveDate` | string (date) | no | Day the order becomes commercially effective. |
| `entityId` | string | yes | UUID of the legal Entity the order belongs to. |
| `excludeFromTaxEngine` | boolean | no | Whether the order is kept out of the tax engine, so no tax is calculated for it. |
| `externalIdentifiers` | array of ExternalIdentifier | no | Identifiers the order carries in other systems, upserted by `fieldName`. |
| `externalIdentifiers[].externalUrl` | string | no | Deep link to the record in the owning system, shown beside the id. |
| `externalIdentifiers[].fieldName` | string | yes | Names the ExternalIdentifierConfiguration this id belongs to, as that configuration spells it. It must already be configured for the tenant; an unknown name is rejected. Identifiers are upserted by this name, so sending it again overwrites the stored value. |
| `externalIdentifiers[].value` | string | yes | The identifier as the owning system spells it. |
| `externalizedInvoicing` | boolean | no | Whether invoicing for this order is handled in another system. Defaults to false. |
| `nonSubscriptionProducts` | array of ProductLine | no | One-time product lines, sitting on the order itself rather than under a subscription. Line types that are not subscription-based belong here — putting one under a subscription is rejected with `INVALID_SUBSCRIPTION_PRODUCT`. |
| `nonSubscriptionProducts[].allowance` | LineAllowance | no | The Allowance & Overage terms of a Commit with Overage line. Required for that line type and rejected on any other; omitted fields default from the product catalog. |
| `nonSubscriptionProducts[].allowance.allowance` | string (decimal) | yes | Committed quantity per allowance term, in the catalog Allowance's unit of measure. |
| `nonSubscriptionProducts[].allowance.allowanceTerm` | string | no | The period the allowance covers: `monthly`, `biMonthly`, `quarterly`, `semiAnnually`, `annually` or `orderTerm`. Defaults to the product catalog's Allowance term. |
| `nonSubscriptionProducts[].allowance.maximumRolloverLimit` | string (decimal) | no | Cap on the quantity that may roll over. Accepted only when the catalog Allowance has rollover enabled, and defaults to its setting. |
| `nonSubscriptionProducts[].allowance.overage` | LineOverage | no | Discount and invoicing overrides for the line's overage. Omit it to take everything from the catalog Overage and its price-book entry. |
| `nonSubscriptionProducts[].allowance.overage.discountAmount` | string (decimal) | no | Discount per unit off the overage price, in the order currency. Takes precedence over `discountPercentage` when both are sent; omit both to price the overage exactly as its price-book entry has it. |
| `nonSubscriptionProducts[].allowance.overage.discountPercentage` | string (decimal) | no | Discount per unit off the overage price on a 0–100 scale, so `10` means ten percent. Used only when `discountAmount` is not given. |
| `nonSubscriptionProducts[].allowance.overage.invoiceTiming` | string | no | Whether the overage invoices `advance` (at the start of its period) or `arrears` (after it). Defaults from the entity's active invoice policy for overage lines, then from the parent line. |
| `nonSubscriptionProducts[].allowance.overage.lineInvoiceDay` | string | no | Which day of the period the overage invoices on: `anniversary`, `first`, `last` or `specific`. Defaults from the entity's active invoice policy for overage lines, then from the parent line. |
| `nonSubscriptionProducts[].allowance.rolloverDuration` | string (decimal) | no | How many allowance terms unused allowance rolls over for. Accepted only when the catalog Allowance has rollover enabled, and defaults to its setting. |
| `nonSubscriptionProducts[].discountAmount` | string (decimal) | no | Discount per unit off `listPrice`, in the order currency. Used when no `unitPrice` is given, and then drives the other two. |
| `nonSubscriptionProducts[].discountPercentage` | string (decimal) | no | Discount per unit off `listPrice` on a 0–100 scale, so `10` means ten percent. Stored as a 0–1 fraction. The lowest precedence of the three — used only when neither `unitPrice` nor `discountAmount` is given. |
| `nonSubscriptionProducts[].externalIdentifiers` | array of ExternalIdentifier | no | Identifiers this line carries in other systems, upserted by `fieldName`. |
| `nonSubscriptionProducts[].externalIdentifiers[].externalUrl` | string | no | Deep link to the record in the owning system, shown beside the id. |
| `nonSubscriptionProducts[].externalIdentifiers[].fieldName` | string | yes | Names the ExternalIdentifierConfiguration this id belongs to, as that configuration spells it. It must already be configured for the tenant; an unknown name is rejected. Identifiers are upserted by this name, so sending it again overwrites the stored value. |
| `nonSubscriptionProducts[].externalIdentifiers[].value` | string | yes | The identifier as the owning system spells it. |
| `nonSubscriptionProducts[].invoiceFrequency` | string | no | How often the line invoices — an InvoiceFrequency member: `monthly`, `biMonthly`, `quarterly`, `semiAnnually`, `annually`, `once`, `custom`, `template`, `milestone`, `allAtOnce` or `termEnd`. Defaults to the product line's own setting. |
| `nonSubscriptionProducts[].invoiceFrequencyTemplateId` | string | no | UUID of the invoice frequency template. |
| `nonSubscriptionProducts[].invoiceTiming` | string | no | Whether the line invoices `advance` (at the start of the service period) or `arrears` (after it). Defaults from the entity's active invoice policy for this line type. |
| `nonSubscriptionProducts[].lineInvoiceDay` | string | no | Which day of the period the line invoices on: `anniversary`, `first`, `last` or `specific`. Defaults from the entity's active invoice policy. |
| `nonSubscriptionProducts[].listPrice` | string (decimal) | no | Undiscounted price per unit, in the order currency. Defaults to the list price the order's price book holds for this product line, and is the figure the other pricing fields are reconciled against. |
| `nonSubscriptionProducts[].priceModel` | string | no | How the line is priced: `flat`, `perUnit`, `tiered`, `volume`, `stairstep` or `rateCard`. Defaults to the product line's own model. `tiered`, `volume` and `stairstep` take their prices from `tiers` and `rateCard` from `rateCardEntries`, so for those the line-level discount fields are not reconciled. `thresholdBased` is rejected: it needs a base line plus a metered usage child, which this endpoint does not build. |
| `nonSubscriptionProducts[].pricingTerm` | string | no | The period the price is quoted for: `monthly`, `biMonthly`, `quarterly`, `semiAnnually`, `annually` or `once`. Defaults to the product line's own term. |
| `nonSubscriptionProducts[].productLineId` | string | yes | UUID of the ProductLine being ordered. It settles the product, the line type and the billing rules every omitted field defaults from. It must exist, and it must not be threshold-based or prepaid: those need a base line plus a metered usage child under one product, which this endpoint does not build. |
| `nonSubscriptionProducts[].quantity` | string (decimal) | no | Number of units ordered. Required unless the line takes its quantity from somewhere else: usage-metered line types (PayAsYouGo, Usage, Billable*) meter it from usage records and tier-priced models (`tiered`, `volume`, `stairstep`, `thresholdBased`) derive it from the tier structure, so for those it defaults to zero. Anywhere else, omitting it fails the request. |
| `nonSubscriptionProducts[].rateCardEntries` | array of RateCardEntry | no | Per-unit rates for a `rateCard`-priced line. Supplying any entry moves pricing onto the rate card, so the line-level discount fields stop being reconciled. |
| `nonSubscriptionProducts[].rateCardEntries[].billableComponentId` | string | no | UUID of the BillableComponent this row meters. Optional — a row may price a unit of measure without naming a component — but a UUID that does not resolve rejects the whole request. |
| `nonSubscriptionProducts[].rateCardEntries[].conversion` | string (decimal) | no | Factor converting the billable unit into the reporting unit — 1000 when usage is metered per call but reported per thousand. |
| `nonSubscriptionProducts[].rateCardEntries[].description` | string | no | Free-text label for the row, carried through to the stored rate-card entry. |
| `nonSubscriptionProducts[].rateCardEntries[].listPrice` | string (decimal) | no | Undiscounted rate per `unitOfMeasure`, in the order currency. Honoured only while the product line's `enforceListPrice` is false; otherwise the product line's own list price wins. |
| `nonSubscriptionProducts[].rateCardEntries[].unitOfMeasure` | string | yes | The unit this row is priced in. |
| `nonSubscriptionProducts[].rateCardEntries[].unitPrice` | string (decimal) | yes | Negotiated rate per `unitOfMeasure`, in the order currency. |
| `nonSubscriptionProducts[].serviceEndDate` | string (date) | no | Last day of the line's service period. The order's own service end is the latest across all its lines, and is left open on an evergreen order. |
| `nonSubscriptionProducts[].serviceStartDate` | string (date) | no | First day of the line's service period. The order's own service start is the earliest across all its lines. |
| `nonSubscriptionProducts[].tags` | array of LineTag | no | Value-accounting tags to allocate this line by. Requires value-allocation tagging to be enabled for sales orders. |
| `nonSubscriptionProducts[].tags[].dimension` | string | yes | Name of the value-accounting dimension, as configured for the tenant. Resolved by name, not by id. |
| `nonSubscriptionProducts[].tags[].value` | string | yes | Name of the value within that dimension. The pair must resolve to an existing tag and the dimension must be enabled for sales orders; an unknown pair rejects the whole request. |
| `nonSubscriptionProducts[].tiers` | array of LineTier | no | The pricing tiers of a `tiered`, `volume` or `stairstep` line. The ranges must be contiguous, and the last tier is left open-ended by omitting its `toQuantity`. |
| `nonSubscriptionProducts[].tiers[].fromQuantity` | string (decimal) | yes | Lower bound of the tier's quantity range, inclusive. |
| `nonSubscriptionProducts[].tiers[].listPrice` | string (decimal) | no | Undiscounted price for this tier, in the order currency. Defaults to `unitPrice`, i.e. no discount on the tier. |
| `nonSubscriptionProducts[].tiers[].priceModel` | string | no | How `unitPrice` applies within the tier: `perUnit` (the default) charges it for every unit in the range, `flat` charges it once for the whole tier. |
| `nonSubscriptionProducts[].tiers[].toQuantity` | string (decimal) | no | Upper bound of the tier's quantity range. Omit it on the last tier to leave the range open-ended. |
| `nonSubscriptionProducts[].tiers[].unitPrice` | string (decimal) | yes | Negotiated price for this tier, in the order currency. Charged per unit in the range when `priceModel` is `perUnit`, or once for the whole tier when it is `flat`. |
| `nonSubscriptionProducts[].totalPrice` | string (decimal) | no | Total for the line, in the order currency. Persisted exactly as supplied rather than recomputed — omit it and the line's own pricing drives the total. |
| `nonSubscriptionProducts[].unitOfMeasure` | string | no | The unit the line is priced and metered in. Defaults to the product line's own unit. |
| `nonSubscriptionProducts[].unitPrice` | string (decimal) | no | Negotiated price per unit, in the order currency. The highest precedence of the three pricing inputs — supply it and `discountAmount` and `discountPercentage` are recomputed from it (discountAmount = listPrice − unitPrice). |
| `oneTimeDiscountAllocation` | string | no | How the one-time discount is spread across the order's invoices: `firstInvoice` puts all of it on the first, `alignToInvoiceFrequency` spreads it over the invoicing schedule, `custom` leaves the allocation to be set by hand. |
| `oneTimeDiscountAmount` | string (decimal) | no | A one-off discount on the order as a whole, in the order currency. Independent of the per-line discount fields. |
| `opportunityId` | string | no | Id of the originating opportunity in the CRM, kept as text. |
| `opportunityName` | string | no | Name of the opportunity the order came out of. |
| `paymentTermCode` | string | no | The order's payment term, named by its code. Defaults to the term on the entity's invoice policy active at `bookDate`. |
| `priceBookId` | string | no | UUID of the PriceBook the lines take their list prices from. Defaults to the standard price book. |
| `purchaseOrder` | string | no | The customer's own purchase-order number for this order. |
| `salesArrangementId` | string | no | UUID of an existing SalesArrangement to file the order under. Omit it and a new arrangement is created for the customer in the order currency. |
| `serviceStartDate` | string (date) | yes | First day of the order's service period. Once the lines are built the order takes the earliest line start instead, so this is what governs an order whose lines carry no dates of their own. |
| `signDate` | string (date) | no | Day the order was signed. |
| `subscriptions` | array of Subscription | no | The recurring part of the order. Each subscription carries its own term and its own product lines. |
| `subscriptions[].autoRenew` | boolean | no | Whether the subscription renews itself at the end of its term. |
| `subscriptions[].externalSubscriptionId` | string | no | The subscription's id in the system that owns it, kept for reconciliation. |
| `subscriptions[].products` | array of SubscriptionProductLine | yes | The recurring product lines of this subscription. Every one must be a subscription-based product line — a one-time line placed here is rejected with `INVALID_SUBSCRIPTION_PRODUCT` and belongs in the order's `nonSubscriptionProducts` instead. |
| `subscriptions[].products[].allowance` | LineAllowance | no | The Allowance & Overage terms of a Commit with Overage line. Required for that line type and rejected on any other; omitted fields default from the product catalog. |
| `subscriptions[].products[].allowance.allowance` | string (decimal) | yes | Committed quantity per allowance term, in the catalog Allowance's unit of measure. |
| `subscriptions[].products[].allowance.allowanceTerm` | string | no | The period the allowance covers: `monthly`, `biMonthly`, `quarterly`, `semiAnnually`, `annually` or `orderTerm`. Defaults to the product catalog's Allowance term. |
| `subscriptions[].products[].allowance.maximumRolloverLimit` | string (decimal) | no | Cap on the quantity that may roll over. Accepted only when the catalog Allowance has rollover enabled, and defaults to its setting. |
| `subscriptions[].products[].allowance.overage` | LineOverage | no | Discount and invoicing overrides for the line's overage. Omit it to take everything from the catalog Overage and its price-book entry. |
| `subscriptions[].products[].allowance.overage.discountAmount` | string (decimal) | no | Discount per unit off the overage price, in the order currency. Takes precedence over `discountPercentage` when both are sent; omit both to price the overage exactly as its price-book entry has it. |
| `subscriptions[].products[].allowance.overage.discountPercentage` | string (decimal) | no | Discount per unit off the overage price on a 0–100 scale, so `10` means ten percent. Used only when `discountAmount` is not given. |
| `subscriptions[].products[].allowance.overage.invoiceTiming` | string | no | Whether the overage invoices `advance` (at the start of its period) or `arrears` (after it). Defaults from the entity's active invoice policy for overage lines, then from the parent line. |
| `subscriptions[].products[].allowance.overage.lineInvoiceDay` | string | no | Which day of the period the overage invoices on: `anniversary`, `first`, `last` or `specific`. Defaults from the entity's active invoice policy for overage lines, then from the parent line. |
| `subscriptions[].products[].allowance.rolloverDuration` | string (decimal) | no | How many allowance terms unused allowance rolls over for. Accepted only when the catalog Allowance has rollover enabled, and defaults to its setting. |
| `subscriptions[].products[].discountAmount` | string (decimal) | no | Discount per unit off `listPrice`, in the order currency. Used when no `unitPrice` is given, and then drives the other two. |
| `subscriptions[].products[].discountPercentage` | string (decimal) | no | Discount per unit off `listPrice` on a 0–100 scale, so `10` means ten percent. Stored as a 0–1 fraction. The lowest precedence of the three — used only when neither `unitPrice` nor `discountAmount` is given. |
| `subscriptions[].products[].externalIdentifiers` | array of ExternalIdentifier | no | Identifiers this line carries in other systems, upserted by `fieldName`. |
| `subscriptions[].products[].externalIdentifiers[].externalUrl` | string | no | Deep link to the record in the owning system, shown beside the id. |
| `subscriptions[].products[].externalIdentifiers[].fieldName` | string | yes | Names the ExternalIdentifierConfiguration this id belongs to, as that configuration spells it. It must already be configured for the tenant; an unknown name is rejected. Identifiers are upserted by this name, so sending it again overwrites the stored value. |
| `subscriptions[].products[].externalIdentifiers[].value` | string | yes | The identifier as the owning system spells it. |
| `subscriptions[].products[].invoiceFrequency` | string | no | How often the line invoices — an InvoiceFrequency member: `monthly`, `biMonthly`, `quarterly`, `semiAnnually`, `annually`, `once`, `custom`, `template`, `milestone`, `allAtOnce` or `termEnd`. Defaults to the product line's own setting. |
| `subscriptions[].products[].invoiceFrequencyTemplateId` | string | no | UUID of the invoice frequency template |
| `subscriptions[].products[].invoiceTiming` | string | no | Whether the line invoices `advance` (at the start of the service period) or `arrears` (after it). Defaults from the entity's active invoice policy for this line type. |
| `subscriptions[].products[].lineInvoiceDay` | string | no | Which day of the period the line invoices on: `anniversary`, `first`, `last` or `specific`. Defaults from the entity's active invoice policy. |
| `subscriptions[].products[].listPrice` | string (decimal) | no | Undiscounted price per unit, in the order currency. Defaults to the list price the order's price book holds for this product line, and is the figure the other pricing fields are reconciled against. |
| `subscriptions[].products[].priceModel` | string | no | How the line is priced: `flat`, `perUnit`, `tiered`, `volume`, `stairstep` or `rateCard`. Defaults to the product line's own model. `tiered`, `volume` and `stairstep` take their prices from `tiers` and `rateCard` from `rateCardEntries`, so for those the line-level discount fields are not reconciled. `thresholdBased` is rejected: it needs a base line plus a metered usage child, which this endpoint does not build. |
| `subscriptions[].products[].pricingTerm` | string | no | The period the price is quoted for: `monthly`, `biMonthly`, `quarterly`, `semiAnnually`, `annually` or `once`. Defaults to the product line's own term. |
| `subscriptions[].products[].productLineId` | string | yes | UUID of the ProductLine being ordered. It settles the product, the line type and the billing rules every omitted field defaults from. It must exist, and it must not be threshold-based or prepaid: those need a base line plus a metered usage child under one product, which this endpoint does not build. |
| `subscriptions[].products[].quantity` | string (decimal) | no | Number of units ordered. Required unless the line takes its quantity from somewhere else: usage-metered line types (PayAsYouGo, Usage, Billable*) meter it from usage records and tier-priced models (`tiered`, `volume`, `stairstep`, `thresholdBased`) derive it from the tier structure, so for those it defaults to zero. Anywhere else, omitting it fails the request. |
| `subscriptions[].products[].rampDeal` | RampDeal | no | Scheduled changes to quantity and price across the line's life. Subscription lines only. |
| `subscriptions[].products[].rampDeal.interval` | string | yes | The granularity the deal steps at: `monthly`, `quarterly` or `annually`. |
| `subscriptions[].products[].rampDeal.invoiceTiming` | string | no | Whether each segment invoices `advance` (at the start of its period) or `arrears` (after it). |
| `subscriptions[].products[].rampDeal.ramps` | array of Ramp | yes | The segments of the deal in order, earliest first. Together they span the subscription line's service period. |
| `subscriptions[].products[].rampDeal.ramps[].invoiceFrequency` | string | no | How often this segment invoices. Defaults to the subscription line's own setting. |
| `subscriptions[].products[].rampDeal.ramps[].invoiceFrequencyTemplateId` | string | no | UUID of the invoice frequency template. |
| `subscriptions[].products[].rampDeal.ramps[].listPrice` | string (decimal) | no | Undiscounted price per unit for this segment, in the order currency. Defaults to `unitPrice`. |
| `subscriptions[].products[].rampDeal.ramps[].quantity` | string (decimal) | yes | Number of units in force for this segment. |
| `subscriptions[].products[].rampDeal.ramps[].serviceEndDate` | string (date) | no | Last day of this segment. Defaults so that the segments run contiguously up to the next one. |
| `subscriptions[].products[].rampDeal.ramps[].serviceStartDate` | string (date) | no | First day of this segment. Defaults so that the segments run contiguously from the subscription line's own start. |
| `subscriptions[].products[].rampDeal.ramps[].unitPrice` | string (decimal) | yes | Negotiated price per unit for this segment, in the order currency. |
| `subscriptions[].products[].rateCardEntries` | array of RateCardEntry | no | Per-unit rates for a `rateCard`-priced line. Supplying any entry moves pricing onto the rate card, so the line-level discount fields stop being reconciled. |
| `subscriptions[].products[].rateCardEntries[].billableComponentId` | string | no | UUID of the BillableComponent this row meters. Optional — a row may price a unit of measure without naming a component — but a UUID that does not resolve rejects the whole request. |
| `subscriptions[].products[].rateCardEntries[].conversion` | string (decimal) | no | Factor converting the billable unit into the reporting unit — 1000 when usage is metered per call but reported per thousand. |
| `subscriptions[].products[].rateCardEntries[].description` | string | no | Free-text label for the row, carried through to the stored rate-card entry. |
| `subscriptions[].products[].rateCardEntries[].listPrice` | string (decimal) | no | Undiscounted rate per `unitOfMeasure`, in the order currency. Honoured only while the product line's `enforceListPrice` is false; otherwise the product line's own list price wins. |
| `subscriptions[].products[].rateCardEntries[].unitOfMeasure` | string | yes | The unit this row is priced in. |
| `subscriptions[].products[].rateCardEntries[].unitPrice` | string (decimal) | yes | Negotiated rate per `unitOfMeasure`, in the order currency. |
| `subscriptions[].products[].serviceEndDate` | string (date) | no | Last day of the line's service period. The order's own service end is the latest across all its lines, and is left open on an evergreen order. |
| `subscriptions[].products[].serviceStartDate` | string (date) | no | First day of the line's service period. The order's own service start is the earliest across all its lines. |
| `subscriptions[].products[].tags` | array of LineTag | no | Value-accounting tags to allocate this line by. Requires value-allocation tagging to be enabled for sales orders. |
| `subscriptions[].products[].tags[].dimension` | string | yes | Name of the value-accounting dimension, as configured for the tenant. Resolved by name, not by id. |
| `subscriptions[].products[].tags[].value` | string | yes | Name of the value within that dimension. The pair must resolve to an existing tag and the dimension must be enabled for sales orders; an unknown pair rejects the whole request. |
| `subscriptions[].products[].tiers` | array of LineTier | no | The pricing tiers of a `tiered`, `volume` or `stairstep` line. The ranges must be contiguous, and the last tier is left open-ended by omitting its `toQuantity`. |
| `subscriptions[].products[].tiers[].fromQuantity` | string (decimal) | yes | Lower bound of the tier's quantity range, inclusive. |
| `subscriptions[].products[].tiers[].listPrice` | string (decimal) | no | Undiscounted price for this tier, in the order currency. Defaults to `unitPrice`, i.e. no discount on the tier. |
| `subscriptions[].products[].tiers[].priceModel` | string | no | How `unitPrice` applies within the tier: `perUnit` (the default) charges it for every unit in the range, `flat` charges it once for the whole tier. |
| `subscriptions[].products[].tiers[].toQuantity` | string (decimal) | no | Upper bound of the tier's quantity range. Omit it on the last tier to leave the range open-ended. |
| `subscriptions[].products[].tiers[].unitPrice` | string (decimal) | yes | Negotiated price for this tier, in the order currency. Charged per unit in the range when `priceModel` is `perUnit`, or once for the whole tier when it is `flat`. |
| `subscriptions[].products[].totalPrice` | string (decimal) | no | Total for the line, in the order currency. Persisted exactly as supplied rather than recomputed — omit it and the line's own pricing drives the total. |
| `subscriptions[].products[].unitOfMeasure` | string | no | The unit the line is priced and metered in. Defaults to the product line's own unit. |
| `subscriptions[].products[].unitPrice` | string (decimal) | no | Negotiated price per unit, in the order currency. The highest precedence of the three pricing inputs — supply it and `discountAmount` and `discountPercentage` are recomputed from it (discountAmount = listPrice − unitPrice). |
| `subscriptions[].serviceEndDate` | string (date) | no | Last day the subscription is in service. Omit it on an evergreen subscription. |
| `subscriptions[].serviceStartDate` | string (date) | yes | First day the subscription is in service. |
| `subscriptions[].term` | string (decimal) | no | Intended length of a `termed` subscription in months. What actually bounds the subscription is `serviceStartDate` and `serviceEndDate`, so supply the end date to fix when it ends. |
| `subscriptions[].termType` | string | yes | `termed` for a subscription of fixed length, `evergreen` for an open-ended one that keeps renewing. A single evergreen subscription makes the whole order evergreen, which leaves the order's service end date open. |

Example:

```json
{
  "approvalStatus": "approved",
  "bookDate": "2026-01-15",
  "currency": "EUR",
  "customer": {
    "additionalContactEmail": "finance@contoso.example",
    "addresses": [
      {
        "attention": "Accounts Payable",
        "city": "München",
        "country": "DE",
        "isDefault": true,
        "line1": "Prinzregentenstraße 22",
        "line2": "4. OG",
        "stateProvince": "Bayern",
        "type": "billTo",
        "zipCode": "80538"
      }
    ],
    "billableTravelExpenseInvoiceDetail": "detailed",
    "businessIdentificationNumber": "HRB 123456",
    "businessType": "company",
    "customerType": "default",
    "defaultCurrency": "EUR",
    "entityId": "1c9e5f38-7b04-4a26-8d31-5f60e2ab7c95",
    "exemptionNumber": "EX-2026-0042",
    "externalIdentifiers": [
      {
        "externalUrl": "https://netsuite.example.com/app/accounting/transactions/salesord.nl?id=10042",
        "fieldName": "netsuiteInternalId",
        "value": "SO-10042"
      }
    ],
    "invoiceConsolidation": "customer",
    "invoiceDeliveryMode": "automated",
    "invoiceEmail": "ap@contoso.example",
    "isExemptFromCreditCardFee": true,
    "mainContactName": "A. Meyer",
    "mainEmail": "hello@contoso.example",
    "name": "Contoso GmbH",
    "parentId": "4d2b8e70-91a3-4c6f-b5d8-1e7a03f9c284",
    "partnerId": "4d2b8e70-91a3-4c6f-b5d8-1e7a03f9c284",
    "partnerType": "reseller",
    "payment": {
      "externalCustomerId": "cus_NffrFeUfNV2Hib",
      "externalPaymentMethodId": "pm_1NGY3s2eZvKYlo2CQmRz9k4T",
      "provider": "Stripe"
    },
    "paymentTermCode": "NET30",
    "preferredLanguage": "english",
    "salesChannel": "salesAssisted",
    "segment": "Enterprise",
    "status": "active",
    "taxExempt": true,
    "taxExemptCode": "RESALE",
    "taxRegistrations": [
      {
        "country": "DE",
        "effectiveDate": "2026-01-01",
        "isActive": true,
        "note": "Filed with the Munich tax office",
        "taxNumber": "DE123456789",
        "type": "VAT"
      }
    ]
  },
  "customerId": "4d2b8e70-91a3-4c6f-b5d8-1e7a03f9c284",
  "dataSource": "Salesforce",
  "effectiveDate": "2026-02-01",
  "entityId": "1c9e5f38-7b04-4a26-8d31-5f60e2ab7c95",
  "excludeFromTaxEngine": true,
  "externalIdentifiers": [
    {
      "externalUrl": "https://netsuite.example.com/app/accounting/transactions/salesord.nl?id=10042",
      "fieldName": "netsuiteInternalId",
      "value": "SO-10042"
    }
  ],
  "externalizedInvoicing": true,
  "nonSubscriptionProducts": [
    {
      "allowance": {
        "allowance": "1000",
        "allowanceTerm": "monthly",
        "maximumRolloverLimit": "500",
        "overage": {
          "discountAmount": "1.50",
          "discountPercentage": "10",
          "invoiceTiming": "arrears",
          "lineInvoiceDay": "anniversary"
        },
        "rolloverDuration": "2"
      },
      "discountAmount": "120.00",
      "discountPercentage": "10",
      "externalIdentifiers": [
        {
          "externalUrl": "https://netsuite.example.com/app/accounting/transactions/salesord.nl?id=10042",
          "fieldName": "netsuiteInternalId",
          "value": "SO-10042"
        }
      ],
      "invoiceFrequency": "monthly",
      "invoiceFrequencyTemplateId": "9f1c2d34-5e6f-4a7b-8c9d-0e1f2a3b4c5d",
      "invoiceTiming": "advance",
      "lineInvoiceDay": "anniversary",
      "listPrice": "1200.00",
      "priceModel": "perUnit",
      "pricingTerm": "annually",
      "productLineId": "6a1f2c94-3d58-4b17-9e02-7c8ab4d5f610",
      "quantity": "10",
      "rateCardEntries": [
        {
          "billableComponentId": "9f1c2d34-5e6f-4a7b-8c9d-0e1f2a3b4c5d",
          "conversion": "1000",
          "description": "API calls beyond the included volume",
          "listPrice": "0.005",
          "unitOfMeasure": "apiCall",
          "unitPrice": "0.004"
        }
      ],
      "serviceEndDate": "2026-12-31",
      "serviceStartDate": "2026-01-01",
      "tags": [
        {
          "dimension": "Department",
          "value": "Engineering"
        }
      ],
      "tiers": [
        {
          "fromQuantity": "0",
          "listPrice": "15.00",
          "priceModel": "perUnit",
          "toQuantity": "100",
          "unitPrice": "12.50"
        }
      ],
      "totalPrice": "12960.00",
      "unitOfMeasure": "seat",
      "unitPrice": "1080.00"
    }
  ],
  "oneTimeDiscountAllocation": "firstInvoice",
  "oneTimeDiscountAmount": "500.00",
  "opportunityId": "0065f00000ABCDEfg",
  "opportunityName": "Contoso — 2026 renewal",
  "paymentTermCode": "NET30",
  "priceBookId": "72fd1a08-6c39-4e5b-83a0-9d4127be6f5c",
  "purchaseOrder": "PO-2026-0042",
  "salesArrangementId": "e30a76b5-c284-4d91-a7f3-2b508c6de194",
  "serviceStartDate": "2026-01-01",
  "signDate": "2026-01-10",
  "subscriptions": [
    {
      "autoRenew": true,
      "externalSubscriptionId": "sub_1NGY3s2eZvKYlo2C",
      "products": [
        {
          "allowance": {
            "allowance": "1000",
            "allowanceTerm": "monthly",
            "maximumRolloverLimit": "500",
            "overage": {
              "discountAmount": null,
              "discountPercentage": null,
              "invoiceTiming": null,
              "lineInvoiceDay": null
            },
            "rolloverDuration": "2"
          },
          "discountAmount": "120.00",
          "discountPercentage": "10",
          "externalIdentifiers": [
            {
              "externalUrl": null,
              "fieldName": null,
              "value": null
            }
          ],
          "invoiceFrequency": "monthly",
          "invoiceFrequencyTemplateId": "9f1c2d34-5e6f-4a7b-8c9d-0e1f2a3b4c5d",
          "invoiceTiming": "advance",
          "lineInvoiceDay": "anniversary",
          "listPrice": "1200.00",
          "priceModel": "perUnit",
          "pricingTerm": "annually",
          "productLineId": "6a1f2c94-3d58-4b17-9e02-7c8ab4d5f610",
          "quantity": "10",
          "rampDeal": {
            "interval": "annually",
            "invoiceTiming": "advance",
            "ramps": [
              null
            ]
          },
          "rateCardEntries": [
            {
              "billableComponentId": null,
              "conversion": null,
              "description": null,
              "listPrice": null,
              "unitOfMeasure": null,
              "unitPrice": null
            }
          ],
          "serviceEndDate": "2026-12-31",
          "serviceStartDate": "2026-01-01",
          "tags": [
            {
              "dimension": null,
              "value": null
            }
          ],
          "tiers": [
            {
              "fromQuantity": null,
              "listPrice": null,
              "priceModel": null,
              "toQuantity": null,
              "unitPrice": null
            }
          ],
          "totalPrice": "12960.00",
          "unitOfMeasure": "seat",
          "unitPrice": "1080.00"
        }
      ],
      "serviceEndDate": "2026-12-31",
      "serviceStartDate": "2026-01-01",
      "term": "12",
      "termType": "termed"
    }
  ]
}
```

## Response

`200` Success

Under `data` (createSalesOrderOutput):

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `salesOrderId` | string | yes | UUID of the new sales order — what every other sales-order route addresses it by. |

## Response headers

- `Idempotency-Replayed` — Present and `true` only when this response replays a previously stored one for the same Idempotency-Key, rather than reflecting a live execution. Absent on a live response - never sent as `false`.

## Errors

| Code | Status |
| --- | --- |
| `INVALID_PRODUCT_LINE` | 400 |
| `INVALID_SUBSCRIPTION_PRODUCT` | 400 |
| `VALIDATION_FAILED` | 400 |
| `NOT_FOUND` | 404 |
| `IDEMPOTENCY_KEY_CONFLICT` | 409 |
| `IDEMPOTENCY_PAYLOAD_MISMATCH` | 422 |

## Statuses

- `400` — Invalid request or business failure. Codes: INVALID_PRODUCT_LINE, INVALID_SUBSCRIPTION_PRODUCT, VALIDATION_FAILED
- `401` — Missing or invalid access token
- `403` — Not authorized
- `404` — Not found. Codes: NOT_FOUND
- `409` — The Idempotency-Key is still being processed by another request.
- `422` — The Idempotency-Key was already used with a different request payload.
- `429` — Too many requests
- `500` — Internal server error
