Skip to main content
A product is something you sell, with a unit price, a currency and an optional recurring interval. Put products on a deal as line items (quantity, discount, monthly or yearly billing) and the deal amount can follow their total. A quote copies a deal’s line items, adds terms and an expiry, and is shared as a public page where the buyer accepts it, and pays when the quote carries a Stripe payment link from your own Stripe account. The MCP server exposes the same through list_products, save_product, list_deal_line_items, update_deal_line_items, list_quotes, create_quote and update_quote. Workflows add products with the Add Product to Deal step.

Scopes

Products, line items and quotes are part of deals: reads need deals:read, writes need deals:write. Line items and quotes follow the deal’s visibility: a credential that can’t see a deal (its pipeline is restricted, or record access limits it to other owners) can’t see its line items or quotes either.

Products

A SKU is unique among products that aren’t archived (409 duplicate_sku). Unit prices take up to four decimals.

Line items

  • A line is in its deal’s currency; it has none of its own. Adding a product priced in another currency needs unit_price in the deal’s currency (400 currency_mismatch otherwise). Changing a deal’s currency doesn’t convert its lines.
  • amount is quantity × unit price less the discount, never below zero, rounded to cents. A percent discount is at most 100; an amount discount is off the whole line.
  • recurring_interval (month, year or null) is the same vocabulary as the deal’s own recurring revenue fields. A recurring line counts one period in totals.total; totals.recurring adds them up per interval and totals.one_time the rest.
  • amount_from_line_items: true makes the deal amount the lines’ total from then on: every change to the lines updates it (and sends deal.updated), and setting amount directly is overridden. The deal’s recurring_amount and recurring_interval follow the recurring lines the same way: when they all bill on one interval the deal takes it and their sum; mixed monthly and yearly lines are converted to the deal’s own interval (×12 or ÷12); with no recurring line, recurring_amount is null. false lets you set both by hand again.
  • A deal has up to 200 lines. A won or lost deal in a pipeline that locks closed deals refuses line changes (422 closed_deal_locked) unless the credential’s owner is a workspace admin.
  • Archived products can’t be added.

Quotes

  • Merging deals moves the merged deal’s quotes to the deal that is kept, so their links keep working; line items stay on the merged deal.
  • Making a quote copies the deal’s line items, its primary contact and company as the buyer (recipient), and the workspace and the credential’s owner as sender. Later changes to the deal don’t change the quote. title defaults to the deal name; terms and expires_at default to the workspace’s quote settings (Settings → Products). expires_at takes a date (YYYY-MM-DD, valid through the end of that day, UTC) or an ISO 8601 date-time, or null for no expiry.
  • Sharing (publish, or publish: true on create) turns a draft into an open quote: public_url starts working and the quote can no longer change. A quote needs at least one line to be shared. With include_payment_link, a Stripe payment link is created in the workspace’s own Stripe account at that moment (payment_link_url); one payment link carries up to 20 lines and can’t mix monthly and yearly lines. When publish: true on create is refused (Stripe, say), the draft still exists: details.quote_id names it.
  • The public page shows the quote’s lines, totals, terms and expiry, and nothing else from your workspace. The buyer accepts with their name and email; the quote becomes accepted with accepted_name, accepted_email and accepted_at, and the page then shows Pay now when the quote has a payment link. An open quote past expires_at shows as expired (display_status: "expired") and can’t be accepted. Views are counted once an hour per viewer, so reloads don’t inflate them (view_count, first_viewed_at, last_viewed_at), and the deal’s owner is notified when the quote is accepted.
  • Voiding makes the page say the quote is no longer available and switches its payment link off in Stripe. A quote whose deal is deleted reads as no longer available too, until the deal is restored.
Payment links use your own Stripe account: payments go straight to it and Orbit never handles the money. A workspace admin connects it in Settings → Products with a Stripe restricted key that has Write access to Payment Links, Prices and Products and nothing else. The key is checked with Stripe, stored encrypted and never returned by any API. Secret keys (sk_...) are refused.

Webhooks

product.*, quote.* and deal.line_item_* events are listed on the events page, all behind deals:read.