Skip to content
SSalesShiftDocs
API reference

Quotes and products

Product catalogue, quotes, line items, the approval chain and quote sending.

17 endpoints· 4 called live· verified against http://127.0.0.1:8741

17 endpoints

GET/api/v1/salesshift/productsCalled liveThe organization's product catalogue, searchable and active-filtered.
Auth
get_org_user
Scope
organization
Success
200 · application/json
Envelope
success+data

Query parameters

NameTypeRequiredNotes
searchstring?optional
active_onlybooleanoptionalDefault true.
limitintegeroptionalDefault 200. max 1000.

Request

curl
curl 'https://api.vxcloud.io/api/v1/salesshift/products' \
  -H "Authorization: Bearer $SS_TOKEN"

Response · 200

Observed shape
{
  "success": "bool",
  "data": [
    {
      "id": "str",
      "sku": "str",
      "name": "str",
      "description": "null",
      "kind": "str",
      "unit_price_cents": "int",
      "unit_price": "str",
      "unit_price_display": "str",
      "currency": "str",
      "cost_cents": "null",
      "billing_interval": "null",
      "billing_interval_count": "int",
      "trial_days": "null",
      "tax_rate": "float",
      "is_active": "bool",
      "created_at": "str"
    },
    "...x2"
  ]
}
Observed on 2026-08-06 — keys are real, values are the types that came back.
#get-api-v1-salesshift-productsquotes_router · vxcloud_fastapiclient/app/services/salesshift/quotes_router.py:252Called against http://127.0.0.1:8741 on 2026-08-06 with a real JWT; the response matched the shape recorded below.
POST/api/v1/salesshift/productsVerified in sessionCreate a catalogue product; recurring products require a billing interval.
Auth
get_org_user
Scope
organization
Success
201 · application/json
Envelope
success+data

Request body (required) — ProductIn

FieldTypeRequiredNotes
skustring?optional
namestringrequired
descriptionstring?optional
kindstringoptionalDefault "one_time".
unit_price_centsintegeroptionalDefault 0.
currencystringoptionalDefault "USD".
cost_centsinteger?optional
billing_intervalstring?optional
billing_interval_countintegeroptionalDefault 1.
trial_daysinteger?optional
tax_ratenumberoptionalDefault 0.
is_activebooleanoptionalDefault true.

Values in the request below are typed placeholders, not sample data.

Request

curl
curl -X POST 'https://api.vxcloud.io/api/v1/salesshift/products' \
  -H "Authorization: Bearer $SS_TOKEN" \
  -H 'Content-Type: application/json' \
  -d '{ "sku": "<string>", "name": "<string>", "description": "<string>", "kind": "one_time", "unit_price_cents": 0, "currency": "USD", "cost_cents": 0, "billing_interval": "<string>", "billing_interval_count": 1, "trial_days": 0, "tax_rate": 0, "is_active": true }'

Response · 201

Top-level keys
{
  "data": "…",
  "success": "…"
}
Top-level keys read from the handler's return statements.

Errors

Statusdetail
400Recurring products need a billing_interval
409A product with that SKU exists
#post-api-v1-salesshift-productsquotes_router · vxcloud_fastapiclient/app/services/salesshift/quotes_router.py:272The quotes feature was verified working against the live system in the session that produced this inventory. This specific route was not re-called during the inventory run.
PATCH/api/v1/salesshift/products/{product_id}Verified in sessionUpdate a catalogue product from a partial payload.
Auth
get_org_user
Scope
organization
Success
200 · application/json
Envelope
success+data

Path parameters

NameTypeRequiredNotes
product_idstring(uuid)required

Request body (required)

Accepts a JSON object; the source declares no field list.

Request

curl
curl -X PATCH 'https://api.vxcloud.io/api/v1/salesshift/products/{product_id}' \
  -H "Authorization: Bearer $SS_TOKEN"

Response · 200

Top-level keys
{
  "data": "…",
  "success": "…"
}
Top-level keys read from the handler's return statements.

Errors

Statusdetail
404Product not found
#patch-api-v1-salesshift-products-product-idquotes_router · vxcloud_fastapiclient/app/services/salesshift/quotes_router.py:299The quotes feature was verified working against the live system in the session that produced this inventory. This specific route was not re-called during the inventory run.
DELETE/api/v1/salesshift/products/{product_id}Verified in sessionSoft delete.

Soft delete. Hard-deleting would orphan the product_id on historical lines, and those lines are financial records.

Auth
get_org_user
Scope
organization
Success
200 · application/json
Envelope
success+custom

Path parameters

NameTypeRequiredNotes
product_idstring(uuid)required

Request

curl
curl -X DELETE 'https://api.vxcloud.io/api/v1/salesshift/products/{product_id}' \
  -H "Authorization: Bearer $SS_TOKEN"

Response · 200

Top-level keys
{
  "deactivated": "…",
  "success": "…"
}
Top-level keys read from the handler's return statements.

Errors

Statusdetail
404Product not found
#delete-api-v1-salesshift-products-product-idquotes_router · vxcloud_fastapiclient/app/services/salesshift/quotes_router.py:326The quotes feature was verified working against the live system in the session that produced this inventory. This specific route was not re-called during the inventory run.
GET/api/v1/salesshift/quotesCalled liveQuotes, optionally filtered by status and deal.
Auth
get_org_user
Scope
organization
Success
200 · application/json
Envelope
success+data

Query parameters

NameTypeRequiredNotes
statusstring?optional
deal_idstring(uuid)?optional
limitintegeroptionalDefault 100. max 500.

Request

curl
curl 'https://api.vxcloud.io/api/v1/salesshift/quotes' \
  -H "Authorization: Bearer $SS_TOKEN"

Response · 200

Observed shape
{
  "success": "bool",
  "data": [
    {
      "id": "str",
      "number": "str",
      "title": "str",
      "deal_id": "null",
      "contact_id": "str",
      "company_id": "null",
      "status": "str",
      "currency": "str",
      "subtotal_cents": "int",
      "discount_cents": "int",
      "tax_cents": "int",
      "total_cents": "int",
      "total_display": "str",
      "reporting_total_cents": "int",
      "fx_rate": "float",
      "fx_as_of": "null",
      "discount_percent": "float",
      "order_discount_kind": "str",
      "order_discount_value": "float",
      "valid_until": "null",
      "terms": "null",
      "notes": "null",
      "contract_doc_id": "str",
      "public_token": "str",
      "sent_at": "str",
      "viewed_at": "null",
      "accepted_at": "str",
      "declined_at": "null",
      "created_at": "str",
      "is_locked": "bool"
    },
    "...x9"
  ]
}
Observed on 2026-08-06 — keys are real, values are the types that came back.
#get-api-v1-salesshift-quotesquotes_router · vxcloud_fastapiclient/app/services/salesshift/quotes_router.py:351Called against http://127.0.0.1:8741 on 2026-08-06 with a real JWT; the response matched the shape recorded below.
POST/api/v1/salesshift/quotesVerified in sessionCreate a quote with its line items and a generated document number.
Auth
get_org_user
Scope
organization
Success
201 · application/json
Envelope
success+data

Request body (required) — QuoteIn

FieldTypeRequiredNotes
titlestring?optional
deal_idstring(uuid)?optional
contact_idstring(uuid)?optional
company_idstring(uuid)?optional
currencystringoptionalDefault "USD".
valid_untilstring(date)?optional
termsstring?optional
notesstring?optional
order_discount_kindstringoptionalDefault "none".
order_discount_valuenumberoptionalDefault 0.
linesLineIn[]optional

Values in the request below are typed placeholders, not sample data.

Request

curl
curl -X POST 'https://api.vxcloud.io/api/v1/salesshift/quotes' \
  -H "Authorization: Bearer $SS_TOKEN" \
  -H 'Content-Type: application/json' \
  -d '{ "title": "<string>", "deal_id": "<string(uuid)>", "contact_id": "<string(uuid)>", "company_id": "<string(uuid)>", "currency": "USD", "valid_until": "<string(date)>", "terms": "<string>", "notes": "<string>", "order_discount_kind": "none", "order_discount_value": 0, "lines": [ "<LineIn>" ] }'

Response · 201

Top-level keys
{
  "data": "…",
  "success": "…"
}
Top-level keys read from the handler's return statements.

Errors

Statusdetail
400computed at runtime: err
400Product {...} not found
#post-api-v1-salesshift-quotesquotes_router · vxcloud_fastapiclient/app/services/salesshift/quotes_router.py:370The quotes feature was verified working against the live system in the session that produced this inventory. This specific route was not re-called during the inventory run.
GET/api/v1/salesshift/quotes/{quote_id}Called liveOne quote with its lines and approval history.
Auth
get_org_user
Scope
organization
Success
200 · application/json
Envelope
success+data

Path parameters

NameTypeRequiredNotes
quote_idstring(uuid)required

Request

curl
curl 'https://api.vxcloud.io/api/v1/salesshift/quotes/{quote_id}' \
  -H "Authorization: Bearer $SS_TOKEN"

Response · 200

Observed shape
{
  "success": "bool",
  "data": {
    "id": "str",
    "number": "str",
    "title": "str",
    "deal_id": "null",
    "contact_id": "str",
    "company_id": "null",
    "status": "str",
    "currency": "str",
    "subtotal_cents": "int",
    "discount_cents": "int",
    "tax_cents": "int",
    "total_cents": "int",
    "total_display": "str",
    "reporting_total_cents": "int",
    "fx_rate": "float",
    "fx_as_of": "null",
    "discount_percent": "float",
    "order_discount_kind": "str",
    "order_discount_value": "float",
    "valid_until": "null",
    "terms": "null",
    "notes": "null",
    "contract_doc_id": "str",
    "public_token": "str",
    "sent_at": "str",
    "viewed_at": "null",
    "accepted_at": "str",
    "declined_at": "null",
    "created_at": "str",
    "is_locked": "bool",
    "lines": [
      {
        "id": "...",
        "product_id": "...",
        "name": "...",
        "description": "...",
        "quantity": "...",
        "unit_price_cents": "...",
        "discount_kind": "...",
        "discount_value": "...",
        "tax_rate": "...",
        "subtotal_cents": "...",
        "discount_cents": "...",
        "tax_cents": "...",
        "total_cents": "...",
        "total_display": "...",
        "billing_interval": "...",
        "billing_interval_count": "...",
        "position": "..."
      },
      "...x2"
    ],
    "approvals": [
      "<empty list>"
    ],
    "approval_required": "null"
  }
}
Observed on 2026-08-06 — keys are real, values are the types that came back.
#get-api-v1-salesshift-quotes-quote-idquotes_router · vxcloud_fastapiclient/app/services/salesshift/quotes_router.py:492Called against http://127.0.0.1:8741 on 2026-08-06 with a real JWT; the response matched the shape recorded below.
PATCH/api/v1/salesshift/quotes/{quote_id}Verified in sessionEdit a quote; sent, accepted and voided quotes are locked.
Auth
get_org_user
Scope
organization
Success
200 · application/json
Envelope
success+data

Path parameters

NameTypeRequiredNotes
quote_idstring(uuid)required

Request body (required) — QuotePatch

FieldTypeRequiredNotes
titlestring?optional
deal_idstring(uuid)?optional
contact_idstring(uuid)?optional
company_idstring(uuid)?optional
currencystring?optional
valid_untilstring(date)?optional
termsstring?optional
notesstring?optional
order_discount_kindstring?optional
order_discount_valuenumber?optional

Values in the request below are typed placeholders, not sample data.

Request

curl
curl -X PATCH 'https://api.vxcloud.io/api/v1/salesshift/quotes/{quote_id}' \
  -H "Authorization: Bearer $SS_TOKEN" \
  -H 'Content-Type: application/json' \
  -d '{ "title": "<string>", "deal_id": "<string(uuid)>", "contact_id": "<string(uuid)>", "company_id": "<string(uuid)>", "currency": "<string>", "valid_until": "<string(date)>", "terms": "<string>", "notes": "<string>", "order_discount_kind": "<string>", "order_discount_value": 0 }'

Response · 200

Top-level keys
{
  "data": "…",
  "success": "…"
}
Top-level keys read from the handler's return statements.

Errors

Statusdetail
400A {...} quote cannot be edited — duplicate it instead
#patch-api-v1-salesshift-quotes-quote-idquotes_router · vxcloud_fastapiclient/app/services/salesshift/quotes_router.py:517The quotes feature was verified working against the live system in the session that produced this inventory. This specific route was not re-called during the inventory run.
DELETE/api/v1/salesshift/quotes/{quote_id}Verified in sessionVoid a quote; an accepted quote cannot be voided.
Auth
get_org_user
Scope
organization
Success
200 · application/json
Envelope
success+data

Path parameters

NameTypeRequiredNotes
quote_idstring(uuid)required

Request

curl
curl -X DELETE 'https://api.vxcloud.io/api/v1/salesshift/quotes/{quote_id}' \
  -H "Authorization: Bearer $SS_TOKEN"

Response · 200

Top-level keys
{
  "data": "…",
  "success": "…"
}
Top-level keys read from the handler's return statements.

Errors

Statusdetail
400An accepted quote cannot be voided
#delete-api-v1-salesshift-quotes-quote-idquotes_router · vxcloud_fastapiclient/app/services/salesshift/quotes_router.py:587The quotes feature was verified working against the live system in the session that produced this inventory. This specific route was not re-called during the inventory run.
POST/api/v1/salesshift/quotes/{quote_id}/approveVerified in sessionApprove a quote that is awaiting approval.
Auth
get_org_user
Scope
organization
Success
200 · application/json
Envelope
success+data

Path parameters

NameTypeRequiredNotes
quote_idstring(uuid)required

Request body (required) — ApprovalDecision

FieldTypeRequiredNotes
reasonstring?optional

Values in the request below are typed placeholders, not sample data.

Request

curl
curl -X POST 'https://api.vxcloud.io/api/v1/salesshift/quotes/{quote_id}/approve' \
  -H "Authorization: Bearer $SS_TOKEN" \
  -H 'Content-Type: application/json' \
  -d '{ "reason": "<string>" }'

Response · 200

Top-level keys
{
  "data": "…",
  "success": "…"
}
Top-level keys read from the handler's return statements.

Errors

Statusdetail
400Quote is not awaiting approval
#post-api-v1-salesshift-quotes-quote-id-approvequotes_router · vxcloud_fastapiclient/app/services/salesshift/quotes_router.py:642The quotes feature was verified working against the live system in the session that produced this inventory. This specific route was not re-called during the inventory run.
POST/api/v1/salesshift/quotes/{quote_id}/decline-approvalVerified in sessionDecline the pending approval on a quote.
Auth
get_org_user
Scope
organization
Success
200 · application/json
Envelope
success+data

Path parameters

NameTypeRequiredNotes
quote_idstring(uuid)required

Request body (required) — ApprovalDecision

FieldTypeRequiredNotes
reasonstring?optional

Values in the request below are typed placeholders, not sample data.

Request

curl
curl -X POST 'https://api.vxcloud.io/api/v1/salesshift/quotes/{quote_id}/decline-approval' \
  -H "Authorization: Bearer $SS_TOKEN" \
  -H 'Content-Type: application/json' \
  -d '{ "reason": "<string>" }'

Response · 200

Top-level keys
{
  "data": "…",
  "success": "…"
}
Top-level keys read from the handler's return statements.
#post-api-v1-salesshift-quotes-quote-id-decline-approvalquotes_router · vxcloud_fastapiclient/app/services/salesshift/quotes_router.py:670The quotes feature was verified working against the live system in the session that produced this inventory. This specific route was not re-called during the inventory run.
PUT/api/v1/salesshift/quotes/{quote_id}/linesVerified in sessionBulk replace — the editor sends the whole grid and the server re-prices it.

Bulk replace — the editor sends the whole grid and the server re-prices it. Simpler and safer than per-line PATCH, which can leave a document half-updated if one call fails.

Auth
get_org_user
Scope
organization
Success
200 · application/json
Envelope
success+data

Path parameters

NameTypeRequiredNotes
quote_idstring(uuid)required

Request body (required)

Untyped Dict[str, Any] body — there is no Pydantic model, so these are the field names the handler actually reads.

lines

Values in the request below are typed placeholders, not sample data.

Request

curl
curl -X PUT 'https://api.vxcloud.io/api/v1/salesshift/quotes/{quote_id}/lines' \
  -H "Authorization: Bearer $SS_TOKEN" \
  -H 'Content-Type: application/json' \
  -d '{ "lines": "<value>" }'

Response · 200

Top-level keys
{
  "data": "…",
  "success": "…"
}
Top-level keys read from the handler's return statements.

Errors

Statusdetail
400A {...} quote cannot be re-priced
400lines must be a list
400Max 200 line items per quote
400computed at runtime: err
#put-api-v1-salesshift-quotes-quote-id-linesquotes_router · vxcloud_fastapiclient/app/services/salesshift/quotes_router.py:539The quotes feature was verified working against the live system in the session that produced this inventory. This specific route was not re-called during the inventory run.
POST/api/v1/salesshift/quotes/{quote_id}/sendVerified in sessionFreeze the quote into a contract document, email it, and hand it to the e-sign flow.

Freeze the quote into a contract document, email it, and hand it to the e-sign flow. Two rules this endpoint exists to keep: * **Sending means delivering.** The quote is only marked `sent` once the mail server has actually accepted the message. When delivery fails the document and its signing link are still created — so the link can be copied and delivered by hand — but the quote stays where it was and the caller is told why, instead of a status that claims a customer has it. * **One document per quote.** A second send reuses the existing `ss_contract_docs` row. Minting a fresh one would leave the first row's signing tokens live on a superseded price, and the customer could sign either of them.

Auth
get_org_user
Scope
organization
Success
200 · application/json
Envelope
success+custom

Path parameters

NameTypeRequiredNotes
quote_idstring(uuid)required

Request body (optional)

Untyped Dict[str, Any] body — there is no Pydantic model, so these are the field names the handler actually reads.

party_a_emailparty_a_nameparty_b_emailparty_b_name

Values in the request below are typed placeholders, not sample data.

Request

curl
curl -X POST 'https://api.vxcloud.io/api/v1/salesshift/quotes/{quote_id}/send' \
  -H "Authorization: Bearer $SS_TOKEN" \
  -H 'Content-Type: application/json' \
  -d '{ "party_a_email": "<value>", "party_a_name": "<value>", "party_b_email": "<value>", "party_b_name": "<value>" }'

Response · 200

Top-level keys
{
  "contract_doc_id": "…",
  "data": "…",
  "delivered": "…",
  "delivery_error": "…",
  "public_url": "…",
  "sent_to": "…",
  "sign_url_b": "…",
  "success": "…"
}
Top-level keys read from the handler's return statements.

Errors

Statusdetail
400Quote is already {...}
400Approval required before sending: {...}
400Add at least one line item
400A recipient email is required to send a quote
409This quote's contract was voided — duplicate the quote to issue a new one
#post-api-v1-salesshift-quotes-quote-id-sendquotes_router · vxcloud_fastapiclient/app/services/salesshift/quotes_router.py:815The quotes feature was verified working against the live system in the session that produced this inventory. This specific route was not re-called during the inventory run.
POST/api/v1/salesshift/quotes/{quote_id}/submit-for-approvalVerified in sessionSubmit a quote for approval, or approve it outright when no rule is triggered.
Auth
get_org_user
Scope
organization
Success
200 · application/json
Envelope
success+custom

Path parameters

NameTypeRequiredNotes
quote_idstring(uuid)required

Request

curl
curl -X POST 'https://api.vxcloud.io/api/v1/salesshift/quotes/{quote_id}/submit-for-approval' \
  -H "Authorization: Bearer $SS_TOKEN"

Response · 200

Top-level keys
{
  "approved_automatically": "…",
  "data": "…",
  "reason": "…",
  "success": "…"
}
Top-level keys read from the handler's return statements.
#post-api-v1-salesshift-quotes-quote-id-submit-for-approvalquotes_router · vxcloud_fastapiclient/app/services/salesshift/quotes_router.py:607The quotes feature was verified working against the live system in the session that produced this inventory. This specific route was not re-called during the inventory run.
GET/api/v1/salesshift/quotes/public/{token}url-secret-onlyVerified in sessionUnauthenticated read for the recipient's link.

Unauthenticated read for the recipient's link. Returns only what belongs on a customer-facing page — no internal ids, owner or FX lineage. `preview=1` is the seller opening their own "Customer view" from the quote editor. Without it, that click stamps `viewed_at` and flips the quote to `viewed` — a read receipt the seller manufactured by looking at their own document, which the list then reports as "with the customer".

Auth
no dependency
Scope
url-secret-only
Success
200 · application/json
Envelope
success+data

The quote's public token is the credential.

Path parameters

NameTypeRequiredNotes
tokenstringrequired

Query parameters

NameTypeRequiredNotes
previewbooleanoptionalDefault false.

Request

curl
curl 'https://api.vxcloud.io/api/v1/salesshift/quotes/public/{token}'

Response · 200

Top-level keys
{
  "data": "…",
  "success": "…"
}
Top-level keys read from the handler's return statements.

Errors

Statusdetail
404Quote not found
#get-api-v1-salesshift-quotes-public-tokenquotes_router · vxcloud_fastapiclient/app/services/salesshift/quotes_router.py:426The quotes feature was verified working against the live system in the session that produced this inventory. This specific route was not re-called during the inventory run.
GET/api/v1/salesshift/settings/quotesCalled liveQuote settings for the organization (numbering, tax, approval thresholds).
Auth
get_org_user
Scope
organization
Success
200 · application/json
Envelope
success+data

Request

curl
curl 'https://api.vxcloud.io/api/v1/salesshift/settings/quotes' \
  -H "Authorization: Bearer $SS_TOKEN"

Response · 200

Observed shape
{
  "success": "bool",
  "data": {
    "reporting_currency": "str",
    "send_pool_rotation_enabled": "bool",
    "quote_approval_discount_pct": "int"
  }
}
Observed on 2026-08-06 — keys are real, values are the types that came back.
#get-api-v1-salesshift-settings-quotesquotes_router · vxcloud_fastapiclient/app/services/salesshift/quotes_router.py:204Called against http://127.0.0.1:8741 on 2026-08-06 with a real JWT; the response matched the shape recorded below.
PUT/api/v1/salesshift/settings/quotesVerified in sessionSet approval thresholds / reporting currency.

Set approval thresholds / reporting currency. Only known keys are stored — an unrecognised key would look configured in the UI while silently doing nothing.

Auth
get_org_user
Scope
organization
Success
200 · application/json
Envelope
success+data

Request body (required)

Accepts a JSON object; the source declares no field list.

Request

curl
curl -X PUT 'https://api.vxcloud.io/api/v1/salesshift/settings/quotes' \
  -H "Authorization: Bearer $SS_TOKEN"

Response · 200

Top-level keys
{
  "data": "…",
  "success": "…"
}
Top-level keys read from the handler's return statements.

Errors

Statusdetail
400Unknown setting(s): {...}
#put-api-v1-salesshift-settings-quotesquotes_router · vxcloud_fastapiclient/app/services/salesshift/quotes_router.py:212The quotes feature was verified working against the live system in the session that produced this inventory. This specific route was not re-called during the inventory run.