Skip to content
SSalesShiftDocs
API reference

Invoicing and payments

The customer's own quote-to-cash: invoices, public invoice pages and PDFs, Stripe checkout, manual payments, subscriptions and the MRR rollup. Not the workspace's own SalesShift bill — that is Plans and your subscription.

22 endpoints· 5 called live· 4 not exercised here· verified against http://127.0.0.1:8741

22 endpoints

GET/api/v1/salesshift/invoicesCalled liveInvoices, optionally filtered by status.
Auth
get_org_user
Scope
organization
Success
200 · application/json
Envelope
success+data

Query parameters

NameTypeRequiredNotes
status_filterstring?optional
limitintegeroptionalDefault 100.

Request

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

Response · 200

Observed shape
{
  "success": "bool",
  "data": [
    {
      "id": "str",
      "number": "str",
      "status": "str",
      "title": "str",
      "quote_id": "null",
      "deal_id": "null",
      "contact_id": "null",
      "company_id": "null",
      "subscription_id": "null",
      "currency": "str",
      "subtotal_cents": "int",
      "discount_cents": "int",
      "tax_cents": "int",
      "total_cents": "int",
      "amount_paid_cents": "int",
      "amount_due_cents": "int",
      "total_display": "str",
      "amount_due_display": "str",
      "reporting_total_cents": "int",
      "fx_rate": "float",
      "fx_as_of": "null",
      "order_discount_kind": "str",
      "order_discount_value": "float",
      "payment_terms": "str",
      "payment_terms_label": "str",
      "po_number": "str",
      "terms": "null",
      "notes": "null",
      "footer_note": "str",
      "bill_to_name": "str",
      "bill_to_email": "str",
      "bill_to_address": "str",
      "bill_to_tax_id": "null",
      "due_date": "str",
      "issued_at": "str",
      "sent_at": "null",
      "paid_at": "null",
      "voided_at": "null",
      "last_pdf_at": "null",
      "pdf_sha256": "null"
    },
    "...x6"
  ]
}
Observed on 2026-08-06 — keys are real, values are the types that came back.
#get-api-v1-salesshift-invoicespayments_router · vxcloud_fastapiclient/app/services/salesshift/payments_router.py:534Called 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/invoicesVerified in sessionCreate a draft invoice with no quote behind it.

Create a draft invoice with no quote behind it. The number is allocated now rather than at issue time, matching quotes. It burns a number on an abandoned draft, which is the deliberate trade: the counter is gapless because finance reads a gap as a missing document, and a voided draft is a better answer to "where did INV-00042 go" than silence.

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

Request body (required) — InvoiceIn

FieldTypeRequiredNotes
titlestring?optional
deal_idstring(uuid)?optional
contact_idstring(uuid)?optional
company_idstring(uuid)?optional
currencystringoptionalDefault "USD".
payment_termsstringoptionalDefault "net_14".
due_datestring(date)?optional
po_numberstring?optional
termsstring?optional
notesstring?optional
footer_notestring?optional
bill_to_namestring?optional
bill_to_emailstring?optional
bill_to_addressstring?optional
bill_to_tax_idstring?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/invoices' \
  -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", "payment_terms": "net_14", "due_date": "<string(date)>", "po_number": "<string>", "terms": "<string>", "notes": "<string>", "footer_note": "<string>", "bill_to_name": "<string>", "bill_to_email": "<string>", "bill_to_address": "<string>", "bill_to_tax_id": "<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
400payment_terms must be one of {...}
400Max {...} line items per invoice
400computed at runtime: err
400Product {...} not found
#post-api-v1-salesshift-invoicespayments_router · vxcloud_fastapiclient/app/services/salesshift/payments_router.py:550The payments 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/invoices/{invoice_id}Called liveOne invoice with its line items.
Auth
get_org_user
Scope
organization
Success
200 · application/json
Envelope
success+data

Path parameters

NameTypeRequiredNotes
invoice_idstring(uuid)required

Request

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

Response · 200

Observed shape
{
  "success": "bool",
  "data": {
    "id": "str",
    "number": "str",
    "status": "str",
    "title": "str",
    "quote_id": "null",
    "deal_id": "null",
    "contact_id": "null",
    "company_id": "null",
    "subscription_id": "null",
    "currency": "str",
    "subtotal_cents": "int",
    "discount_cents": "int",
    "tax_cents": "int",
    "total_cents": "int",
    "amount_paid_cents": "int",
    "amount_due_cents": "int",
    "total_display": "str",
    "amount_due_display": "str",
    "reporting_total_cents": "int",
    "fx_rate": "float",
    "fx_as_of": "null",
    "order_discount_kind": "str",
    "order_discount_value": "float",
    "payment_terms": "str",
    "payment_terms_label": "str",
    "po_number": "str",
    "terms": "null",
    "notes": "null",
    "footer_note": "str",
    "bill_to_name": "str",
    "bill_to_email": "str",
    "bill_to_address": "str",
    "bill_to_tax_id": "null",
    "due_date": "str",
    "issued_at": "str",
    "sent_at": "null",
    "paid_at": "null",
    "voided_at": "null",
    "last_pdf_at": "null",
    "pdf_sha256": "null"
  }
}
Observed on 2026-08-06 — keys are real, values are the types that came back.

Errors

Statusdetail
404Invoice not found
#get-api-v1-salesshift-invoices-invoice-idpayments_router · vxcloud_fastapiclient/app/services/salesshift/payments_router.py:701Called 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/invoices/{invoice_id}Verified in sessionEdit the header.

Edit the header. Money is always recomputed server-side afterwards — a currency or header-discount change re-prices every line.

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

Path parameters

NameTypeRequiredNotes
invoice_idstring(uuid)required

Request body (required) — InvoicePatch

FieldTypeRequiredNotes
titlestring?optional
deal_idstring(uuid)?optional
contact_idstring(uuid)?optional
company_idstring(uuid)?optional
currencystring?optional
payment_termsstring?optional
due_datestring(date)?optional
po_numberstring?optional
termsstring?optional
notesstring?optional
footer_notestring?optional
bill_to_namestring?optional
bill_to_emailstring?optional
bill_to_addressstring?optional
bill_to_tax_idstring?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/invoices/{invoice_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>", "payment_terms": "<string>", "due_date": "<string(date)>", "po_number": "<string>", "terms": "<string>", "notes": "<string>", "footer_note": "<string>", "bill_to_name": "<string>", "bill_to_email": "<string>", "bill_to_address": "<string>", "bill_to_tax_id": "<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
400payment_terms must be one of {...}
#patch-api-v1-salesshift-invoices-invoice-idpayments_router · vxcloud_fastapiclient/app/services/salesshift/payments_router.py:729The payments 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/invoices/{invoice_id}/checkoutVerified in sessionCreate a Stripe Checkout Session for the amount still outstanding.
Auth
get_org_user
Scope
organization
Success
200 · application/json
Envelope
success+custom

Path parameters

NameTypeRequiredNotes
invoice_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.

return_base

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

Request

curl
curl -X POST 'https://api.vxcloud.io/api/v1/salesshift/invoices/{invoice_id}/checkout' \
  -H "Authorization: Bearer $SS_TOKEN" \
  -H 'Content-Type: application/json' \
  -d '{ "return_base": "<value>" }'

Response · 200

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

Errors

Statusdetail
404Invoice not found
400Invoice is already {...}
400Nothing left to pay
400Connect Stripe under Settings → Integrations first
502Stripe rejected the request: {...}
#post-api-v1-salesshift-invoices-invoice-id-checkoutpayments_router · vxcloud_fastapiclient/app/services/salesshift/payments_router.py:991The payments 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/invoices/{invoice_id}/issueVerified in sessionDraft → open: stamp the issue date, derive the due date from the payment term and freeze the bill-to snapshot.

Draft → open: stamp the issue date, derive the due date from the payment term and freeze the bill-to snapshot.

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

Path parameters

NameTypeRequiredNotes
invoice_idstring(uuid)required

Request body (optional) — InvoiceIssueIn

FieldTypeRequiredNotes
due_datestring(date)?optional

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

Request

curl
curl -X POST 'https://api.vxcloud.io/api/v1/salesshift/invoices/{invoice_id}/issue' \
  -H "Authorization: Bearer $SS_TOKEN" \
  -H 'Content-Type: application/json' \
  -d '{ "due_date": "<string(date)>" }'

Response · 200

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

Errors

Statusdetail
409Invoice {...} is already {...}
#post-api-v1-salesshift-invoices-invoice-id-issuepayments_router · vxcloud_fastapiclient/app/services/salesshift/payments_router.py:812The payments 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/invoices/{invoice_id}/linesVerified in sessionBulk replace, same contract as the quote editor: the client sends the whole grid and the server re-prices it, so a failed call can never leave the document half-updated.

Bulk replace, same contract as the quote editor: the client sends the whole grid and the server re-prices it, so a failed call can never leave the document half-updated.

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

Path parameters

NameTypeRequiredNotes
invoice_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/invoices/{invoice_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
400lines must be a list
400Max {...} line items per invoice
400computed at runtime: err
#put-api-v1-salesshift-invoices-invoice-id-linespayments_router · vxcloud_fastapiclient/app/services/salesshift/payments_router.py:769The payments 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/invoices/{invoice_id}/pdfVerified in sessionRender and return the invoice PDF inline.
Auth
get_org_user
Scope
organization
Success
200 · application/json
Envelope
binary

Path parameters

NameTypeRequiredNotes
invoice_idstring(uuid)required

Request

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

Response · 200

Response carrying the invoice PDF inline (application/pdf).

#get-api-v1-salesshift-invoices-invoice-id-pdfpayments_router · vxcloud_fastapiclient/app/services/salesshift/payments_router.py:868The payments 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/invoices/{invoice_id}/record-paymentVerified in sessionBank transfer / cheque / cash.

Bank transfer / cheque / cash. Same ledger, provider='manual'.

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

Path parameters

NameTypeRequiredNotes
invoice_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.

amount_centsproviderreference

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

Request

curl
curl -X POST 'https://api.vxcloud.io/api/v1/salesshift/invoices/{invoice_id}/record-payment' \
  -H "Authorization: Bearer $SS_TOKEN" \
  -H 'Content-Type: application/json' \
  -d '{ "amount_cents": "<value>", "provider": "<value>", "reference": "<value>" }'

Response · 200

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

Errors

Statusdetail
404Invoice not found
400amount_cents must be positive
#post-api-v1-salesshift-invoices-invoice-id-record-paymentpayments_router · vxcloud_fastapiclient/app/services/salesshift/payments_router.py:1066The payments 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/invoices/{invoice_id}/sendVerified in sessionEmail the invoice with the PDF attached, from the tenant's own mailbox.

Email the invoice with the PDF attached, from the tenant's own mailbox. `outbound.send_mail` rather than the campaign sender: an invoice must not be blocked by a marketing opt-out, must not carry an unsubscribe footer, and — the reason the others cannot be used at all — must carry an attachment.

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

Path parameters

NameTypeRequiredNotes
invoice_idstring(uuid)required

Request body (optional) — InvoiceSendIn

FieldTypeRequiredNotes
to_emailstring?optional
ccstring[]optional
subjectstring?optional
messagestring?optional
account_idstring(uuid)?optional

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

Request

curl
curl -X POST 'https://api.vxcloud.io/api/v1/salesshift/invoices/{invoice_id}/send' \
  -H "Authorization: Bearer $SS_TOKEN" \
  -H 'Content-Type: application/json' \
  -d '{ "to_email": "<string>", "cc": [ "<string>" ], "subject": "<string>", "message": "<string>", "account_id": "<string(uuid)>" }'

Response · 200

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

Errors

Statusdetail
409A void invoice cannot be sent
400A recipient email is required to send an invoice
502computed at runtime: result.error
#post-api-v1-salesshift-invoices-invoice-id-sendpayments_router · vxcloud_fastapiclient/app/services/salesshift/payments_router.py:884The payments 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/invoices/{invoice_id}/voidVerified in sessionCancel an invoice that was never paid.

Cancel an invoice that was never paid. An invoice with money against it is NOT voidable — voiding would erase the obligation the payment was made against and leave the ledger holding cash for a document that no longer exists. That correction is a refund plus a credit note.

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

Path parameters

NameTypeRequiredNotes
invoice_idstring(uuid)required

Request body (optional)

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

Request

curl
curl -X POST 'https://api.vxcloud.io/api/v1/salesshift/invoices/{invoice_id}/void' \
  -H "Authorization: Bearer $SS_TOKEN"

Response · 200

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

Errors

Statusdetail
409{...} has been paid against this invoice — refund it and raise a credit note instead of voiding
#post-api-v1-salesshift-invoices-invoice-id-voidpayments_router · vxcloud_fastapiclient/app/services/salesshift/payments_router.py:832The payments 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/invoices/from-quote/{quote_id}Verified in sessionCreate an invoice from an accepted quote.
Auth
get_org_user
Scope
organization
Success
201 · application/json
Envelope
success+data

Path parameters

NameTypeRequiredNotes
quote_idstring(uuid)required

Request

curl
curl -X POST 'https://api.vxcloud.io/api/v1/salesshift/invoices/from-quote/{quote_id}' \
  -H "Authorization: Bearer $SS_TOKEN"

Response · 201

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

Errors

Statusdetail
404Quote not found
400Only an accepted quote can be invoiced
#post-api-v1-salesshift-invoices-from-quote-quote-idpayments_router · vxcloud_fastapiclient/app/services/salesshift/payments_router.py:619The payments 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/invoices/public/{token}url-secret-onlyVerified in sessionPublic invoice view addressed by its share token.
Auth
no dependency
Scope
url-secret-only
Success
200 · application/json
Envelope
success+data

The invoice's public_token is the credential.

Path parameters

NameTypeRequiredNotes
tokenstringrequired

Request

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

Response · 200

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

Errors

Statusdetail
404Invoice not found
#get-api-v1-salesshift-invoices-public-tokenpayments_router · vxcloud_fastapiclient/app/services/salesshift/payments_router.py:641The payments 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/invoices/public/{token}/pdfurl-secret-onlyVerified in sessionThe customer's own copy.

The customer's own copy. Unauthenticated on purpose: the token IS the credential, exactly as it is for GET /invoices/public/{token}.

Auth
no dependency
Scope
url-secret-only
Success
200 · application/json
Envelope
binary

The invoice's public_token is the credential.

Path parameters

NameTypeRequiredNotes
tokenstringrequired

Request

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

Response · 200

Response carrying the invoice PDF (application/pdf).

Errors

Statusdetail
404Invoice not found
#get-api-v1-salesshift-invoices-public-token-pdfpayments_router · vxcloud_fastapiclient/app/services/salesshift/payments_router.py:681The payments 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/subscriptionsCalled liveThe organization's subscriptions, optionally filtered by status.
Auth
get_org_user
Scope
organization
Success
200 · application/json
Envelope
success+data

Query parameters

NameTypeRequiredNotes
statusstring?optional

Request

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

Response · 200

Observed shape
{
  "success": "bool",
  "data": [
    {
      "id": "str",
      "status": "str",
      "currency": "str",
      "contact_id": "str",
      "company_id": "null",
      "quote_id": "str",
      "mrr_cents": "int",
      "arr_cents": "int",
      "mrr_display": "str",
      "reporting_mrr_cents": "int",
      "billing_interval": "str",
      "billing_interval_count": "int",
      "current_period_start": "str",
      "current_period_end": "str",
      "started_at": "str",
      "trial_end": "null",
      "canceled_at": "null",
      "cancel_reason": "null",
      "provider": "null",
      "provider_ref": "null"
    },
    "...x2"
  ]
}
Observed on 2026-08-06 — keys are real, values are the types that came back.
#get-api-v1-salesshift-subscriptionssubscriptions_router · vxcloud_fastapiclient/app/services/salesshift/subscriptions_router.py:186Called 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/subscriptionsNot exercised hereCreate a recurring subscription for a contact or company.
Auth
get_org_user
Scope
organization
Success
201 · application/json
Envelope
success+data

Request body (required)

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

billing_intervalbilling_interval_countcompany_idcontact_idcurrencyitemsnotestatus

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

Request

curl
curl -X POST 'https://api.vxcloud.io/api/v1/salesshift/subscriptions' \
  -H "Authorization: Bearer $SS_TOKEN" \
  -H 'Content-Type: application/json' \
  -d '{ "billing_interval": "<value>", "billing_interval_count": "<value>", "company_id": "<value>", "contact_id": "<value>", "currency": "<value>", "items": "<value>", "note": "<value>", "status": "<value>" }'

Response · 201

Top-level keys
{
  "data": "…",
  "success": "…"
}
Top-level keys read from the handler's return statements.
#post-api-v1-salesshift-subscriptionssubscriptions_router · vxcloud_fastapiclient/app/services/salesshift/subscriptions_router.py:202Implemented and read in source for this inventory, but not exercised against a running server here. Treat as undemonstrated until someone calls it.
GET/api/v1/salesshift/subscriptions/{subscription_id}Called liveOne subscription by id.
Auth
get_org_user
Scope
organization
Success
200 · application/json
Envelope
success+data

Path parameters

NameTypeRequiredNotes
subscription_idstring(uuid)required

Request

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

Response · 200

Observed shape
{
  "success": "bool",
  "data": {
    "id": "str",
    "status": "str",
    "currency": "str",
    "contact_id": "str",
    "company_id": "null",
    "quote_id": "str",
    "mrr_cents": "int",
    "arr_cents": "int",
    "mrr_display": "str",
    "reporting_mrr_cents": "int",
    "billing_interval": "str",
    "billing_interval_count": "int",
    "current_period_start": "str",
    "current_period_end": "str",
    "started_at": "str",
    "trial_end": "null",
    "canceled_at": "null",
    "cancel_reason": "null",
    "provider": "null",
    "provider_ref": "null",
    "items": [
      {
        "id": "...",
        "name": "...",
        "quantity": "...",
        "unit_price_cents": "...",
        "product_id": "..."
      },
      "...x1"
    ],
    "mrr_movements": [
      {
        "id": "...",
        "occurred_on": "...",
        "kind": "...",
        "delta_cents": "...",
        "delta_display": "...",
        "note": "..."
      },
      "...x5"
    ]
  }
}
Observed on 2026-08-06 — keys are real, values are the types that came back.

Errors

Statusdetail
404Subscription not found
#get-api-v1-salesshift-subscriptions-subscription-idsubscriptions_router · vxcloud_fastapiclient/app/services/salesshift/subscriptions_router.py:414Called 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/subscriptions/{subscription_id}Not exercised hereChange the plan value.

Change the plan value. The MRR delta is booked as expansion/contraction automatically from the direction of the change.

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

Path parameters

NameTypeRequiredNotes
subscription_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.

mrr_centsnote

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

Request

curl
curl -X PATCH 'https://api.vxcloud.io/api/v1/salesshift/subscriptions/{subscription_id}' \
  -H "Authorization: Bearer $SS_TOKEN" \
  -H 'Content-Type: application/json' \
  -d '{ "mrr_cents": "<value>", "note": "<value>" }'

Response · 200

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

Errors

Statusdetail
404Subscription not found
400MRR cannot be negative
#patch-api-v1-salesshift-subscriptions-subscription-idsubscriptions_router · vxcloud_fastapiclient/app/services/salesshift/subscriptions_router.py:250Implemented and read in source for this inventory, but not exercised against a running server here. Treat as undemonstrated until someone calls it.
POST/api/v1/salesshift/subscriptions/{subscription_id}/cancelNot exercised hereCancel a subscription, immediately or at period end.
Auth
get_org_user
Scope
organization
Success
200 · application/json
Envelope
success+custom

Path parameters

NameTypeRequiredNotes
subscription_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.

reason

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

Request

curl
curl -X POST 'https://api.vxcloud.io/api/v1/salesshift/subscriptions/{subscription_id}/cancel' \
  -H "Authorization: Bearer $SS_TOKEN" \
  -H 'Content-Type: application/json' \
  -d '{ "reason": "<value>" }'

Response · 200

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

Errors

Statusdetail
404Subscription not found
#post-api-v1-salesshift-subscriptions-subscription-id-cancelsubscriptions_router · vxcloud_fastapiclient/app/services/salesshift/subscriptions_router.py:280Implemented and read in source for this inventory, but not exercised against a running server here. Treat as undemonstrated until someone calls it.
POST/api/v1/salesshift/subscriptions/{subscription_id}/reactivateNot exercised hereReactivate a cancelled subscription.
Auth
get_org_user
Scope
organization
Success
200 · application/json
Envelope
success+data

Path parameters

NameTypeRequiredNotes
subscription_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.

mrr_centsnote

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

Request

curl
curl -X POST 'https://api.vxcloud.io/api/v1/salesshift/subscriptions/{subscription_id}/reactivate' \
  -H "Authorization: Bearer $SS_TOKEN" \
  -H 'Content-Type: application/json' \
  -d '{ "mrr_cents": "<value>", "note": "<value>" }'

Response · 200

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

Errors

Statusdetail
404Subscription not found
400Reactivating needs a positive mrr_cents
#post-api-v1-salesshift-subscriptions-subscription-id-reactivatesubscriptions_router · vxcloud_fastapiclient/app/services/salesshift/subscriptions_router.py:329Implemented and read in source for this inventory, but not exercised against a running server here. Treat as undemonstrated until someone calls it.
GET/api/v1/salesshift/subscriptions/mrrCalled liveThe MRR waterfall — what the ledger exists for.
Auth
get_org_user
Scope
organization
Success
200 · application/json
Envelope
success+data

Query parameters

NameTypeRequiredNotes
monthsintegeroptionalDefault 12. max 60.

Request

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

Response · 200

Observed shape
{
  "success": "bool",
  "data": {
    "current_mrr_cents": "int",
    "current_arr_cents": "int",
    "ledger_total_cents": "int",
    "ledger_agrees_with_cache": "bool",
    "waterfall": [
      {
        "month": "...",
        "new": "...",
        "contraction": "...",
        "expansion": "...",
        "reactivation": "...",
        "churn": "...",
        "closing_mrr_cents": "..."
      },
      "...x1"
    ]
  }
}
Observed on 2026-08-06 — keys are real, values are the types that came back.
#get-api-v1-salesshift-subscriptions-mrrsubscriptions_router · vxcloud_fastapiclient/app/services/salesshift/subscriptions_router.py:356Called 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/webhooks/stripe/{integration_id}signature-verifiedVerified in sessionStripe webhook receiver for one payment integration.
Auth
no dependency
Scope
signature-verified
Success
200 · application/json
Envelope
bare-object

The Stripe-Signature header is verified against the endpoint secret stored on the integration. With no secret stored the route refuses (500) rather than trusting the body; a bad signature is 400 'Invalid signature'.

Path parameters

NameTypeRequiredNotes
integration_idstring(uuid)required

Request

curl
curl -X POST 'https://api.vxcloud.io/api/v1/salesshift/webhooks/stripe/{integration_id}'

Response · 200

Top-level keys
{
  "duplicate": "…",
  "handled": "…",
  "received": "…"
}
Top-level keys read from the handler's return statements.

Errors

Statusdetail
404Unknown payment integration
503Webhook secret not configured for this integration
400Invalid signature
#post-api-v1-salesshift-webhooks-stripe-integration-idpayments_router · vxcloud_fastapiclient/app/services/salesshift/payments_router.py:1162The payments 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.