Skip to content
SSalesShiftDocs
API reference

Contracts and e-sign

Contract documents, public token signing, audit trail and executed PDFs.

12 endpoints· 2 called live· verified against http://127.0.0.1:8741

12 endpoints

GET/api/v1/salesshift/contractsCalled liveThe organization's contract documents, optionally filtered by deal.
Auth
get_org_user
Scope
organization
Success
200 · application/json
Envelope
success+data

Query parameters

NameTypeRequiredNotes
deal_idstring(uuid)?optional

Request

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

Response · 200

Observed shape
{
  "success": "bool",
  "data": [
    {
      "id": "str",
      "deal_id": "null",
      "title": "str",
      "status": "str",
      "party_a_name": "str",
      "party_a_email": "str",
      "party_b_name": "str",
      "party_b_email": "str",
      "signed_a_at": "null",
      "signed_b_at": "null",
      "signature_a": "null",
      "signature_b": "null",
      "sent_at": "null",
      "completed_at": "null",
      "created_at": "str",
      "content_html": "str",
      "fields": {
        "pages": "...",
        "fields": "..."
      },
      "content_sha256": "null",
      "token_expires_at": "null",
      "declined_at": "null",
      "declined_by": "null",
      "decline_reason": "null",
      "reminder_count": "int",
      "last_reminded_at": "null",
      "signer_a_claimed_name": "null",
      "signer_b_claimed_name": "null",
      "sign_url_a": "str",
      "sign_url_b": "str",
      "pdf_url_a": "str",
      "pdf_url_b": "str"
    },
    "...x23"
  ]
}
Observed on 2026-08-06 — keys are real, values are the types that came back.
#get-api-v1-salesshift-contractscontracts_router · vxcloud_fastapiclient/app/services/salesshift/contracts_router.py:1231Called 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/contractsVerified in sessionCreate a contract document with its signing parties.
Auth
get_org_user
Scope
organization
Success
201 · application/json
Envelope
success+data

Request body (required) — ContractIn

FieldTypeRequiredNotes
deal_idstring(uuid)?optional
titlestringrequired≤ 300 chars.
content_htmlstringoptionalDefault "".
party_a_namestringoptionalDefault "".
party_a_emailstringoptionalDefault "".
party_b_namestringoptionalDefault "".
party_b_emailstringoptionalDefault "".

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

Request

curl
curl -X POST 'https://api.vxcloud.io/api/v1/salesshift/contracts' \
  -H "Authorization: Bearer $SS_TOKEN" \
  -H 'Content-Type: application/json' \
  -d '{ "deal_id": "<string(uuid)>", "title": "<string>", "content_html": "", "party_a_name": "", "party_a_email": "", "party_b_name": "", "party_b_email": "" }'

Response · 201

Top-level keys
{
  "data": "…",
  "success": "…"
}
Top-level keys read from the handler's return statements.
#post-api-v1-salesshift-contractscontracts_router · vxcloud_fastapiclient/app/services/salesshift/contracts_router.py:1253The contracts 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/contracts/{contract_id}Verified in sessionEdit a contract.

Edit a contract. The body, the title and the field layout are frozen the moment anyone signs — that is what makes the content hash meaningful. Correcting the counterparty's name or address stays open while THAT party has not signed, because fixing a typo in an email address is the single most common real envelope correction and voiding for it is absurd.

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

Path parameters

NameTypeRequiredNotes
contract_idstring(uuid)required

Request body (required) — ContractPatch

FieldTypeRequiredNotes
titlestring?optional
content_htmlstring?optional
party_b_namestring?optional
party_b_emailstring?optional
fieldsobject?optional

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

Request

curl
curl -X PATCH 'https://api.vxcloud.io/api/v1/salesshift/contracts/{contract_id}' \
  -H "Authorization: Bearer $SS_TOKEN" \
  -H 'Content-Type: application/json' \
  -d '{ "title": "<string>", "content_html": "<string>", "party_b_name": "<string>", "party_b_email": "<string>", "fields": {} }'

Response · 200

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

Errors

Statusdetail
404Contract not found
400Contract is {...} and can no longer be edited
400Contract already has signatures — {...} can no longer be changed. Void it and draft a new one instead.
400The counterparty has already signed
#patch-api-v1-salesshift-contracts-contract-idcontracts_router · vxcloud_fastapiclient/app/services/salesshift/contracts_router.py:1605The contracts 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/contracts/{contract_id}Verified in sessionVoid a contract document.
Auth
get_org_user
Scope
organization
Success
200 · application/json
Envelope
success+custom

Path parameters

NameTypeRequiredNotes
contract_idstring(uuid)required

Request

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

Response · 200

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

Errors

Statusdetail
404Contract not found
400A fully executed contract cannot be voided. Sign a termination or amendment agreement instead.
#delete-api-v1-salesshift-contracts-contract-idcontracts_router · vxcloud_fastapiclient/app/services/salesshift/contracts_router.py:1765The contracts 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/contracts/{contract_id}/auditCalled liveThe audit trail behind the Certificate of Completion, for the sender's own dashboard — the same rows the certificate page prints.

The audit trail behind the Certificate of Completion, for the sender's own dashboard — the same rows the certificate page prints.

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

Path parameters

NameTypeRequiredNotes
contract_idstring(uuid)required

Request

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

Response · 200

Observed shape
{
  "success": "bool",
  "content_sha256": "null",
  "executed_pdf_sha256": "null",
  "consent_a_at": "null",
  "consent_b_at": "null",
  "consent_a_text": "null",
  "consent_b_text": "null",
  "signer_a_ip": "null",
  "signer_b_ip": "null",
  "signer_a_user_agent": "null",
  "signer_b_user_agent": "null",
  "signer_a_claimed_name": "null",
  "signer_b_claimed_name": "null",
  "data": [
    {
      "at": "str",
      "party": "str",
      "event": "str",
      "ip": "str",
      "user_agent": "str",
      "detail": {
        "by": "...",
        "title": "..."
      }
    },
    "...x3"
  ]
}
Observed on 2026-08-06 — keys are real, values are the types that came back.

Errors

Statusdetail
404Contract not found
#get-api-v1-salesshift-contracts-contract-id-auditcontracts_router · vxcloud_fastapiclient/app/services/salesshift/contracts_router.py:1796Called against http://127.0.0.1:8741 on 2026-08-06 with a real JWT; the response matched the shape recorded below.
GET/api/v1/salesshift/contracts/{contract_id}/pdfVerified in sessionThe sender's own copy.

The sender's own copy. Without this the only route to an executed contract was the public signing surface — the org had to open a counterparty link to retrieve its own artifact.

Auth
get_org_user
Scope
organization
Success
200 · application/json
Envelope
binary

Path parameters

NameTypeRequiredNotes
contract_idstring(uuid)required

Request

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

Response · 200

Response carrying the contract PDF (application/pdf).

Errors

Statusdetail
404Contract not found
502PDF engine unavailable: {...}
#get-api-v1-salesshift-contracts-contract-id-pdfcontracts_router · vxcloud_fastapiclient/app/services/salesshift/contracts_router.py:1827The contracts 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/contracts/{contract_id}/sendVerified in sessionDeliver signing links by real email — and, on a re-send, chase only the party who still owes a signature.

Deliver signing links by real email — and, on a re-send, chase only the party who still owes a signature.

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

Path parameters

NameTypeRequiredNotes
contract_idstring(uuid)required

Request

curl
curl -X POST 'https://api.vxcloud.io/api/v1/salesshift/contracts/{contract_id}/send' \
  -H "Authorization: Bearer $SS_TOKEN"

Response · 200

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

Errors

Statusdetail
404Contract not found
400Contract is {...} and cannot be sent again
400Counterparty email required before sending
400Your own email is required before sending — party A would have no way to sign
#post-api-v1-salesshift-contracts-contract-id-sendcontracts_router · vxcloud_fastapiclient/app/services/salesshift/contracts_router.py:1674The contracts 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/contracts/sign/{token}url-secret-onlyVerified in sessionPublic signing view for a token; records the first open per party as a 'viewed' audit event.
Auth
no dependency
Scope
url-secret-only
Success
200 · application/json
Envelope
success+custom

The signing token is the credential; it selects the document AND the party.

Path parameters

NameTypeRequiredNotes
tokenstringrequired

Request

curl
curl 'https://api.vxcloud.io/api/v1/salesshift/contracts/sign/{token}'

Response · 200

Top-level keys
{
  "already_signed": "…",
  "audit": "…",
  "can_sign": "…",
  "consent_text": "…",
  "doc": "…",
  "envelope_id": "…",
  "mode": "…",
  "other_signed": "…",
  "party": "…",
  "party_email": "…",
  "party_name": "…",
  "success": "…"
}
Top-level keys read from the handler's return statements.
#get-api-v1-salesshift-contracts-sign-tokencontracts_router · vxcloud_fastapiclient/app/services/salesshift/contracts_router.py:681The contracts 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/contracts/sign/{token}url-secret-onlyVerified in sessionPublic signature submit; row-locked so two simultaneous posts cannot both sign.
Auth
no dependency
Scope
url-secret-only
Success
200 · application/json
Envelope
success+custom

The signing token is the credential. The row is locked FOR UPDATE so two simultaneous posts cannot both sign.

Path parameters

NameTypeRequiredNotes
tokenstringrequired

Request body (required) — SignIn

FieldTypeRequiredNotes
namestringrequired≤ 200 chars.
signaturestringrequired≤ 300 chars.
stylestringoptionalDefault "".
image_datastringoptionalDefault "". ≤ 300000 chars.
initialsstringoptionalDefault "". ≤ 20 chars.
field_valuesobjectoptional
consentboolean?optional
consent_textstringoptionalDefault "". ≤ 2000 chars.

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

Request

curl
curl -X POST 'https://api.vxcloud.io/api/v1/salesshift/contracts/sign/{token}' \
  -H 'Content-Type: application/json' \
  -d '{ "name": "<string>", "signature": "<string>", "style": "", "image_data": "", "initials": "", "field_values": {}, "consent": false, "consent_text": "" }'

Response · 200

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

Errors

Statusdetail
400This link is for downloading the executed copy only
400Contract already {...}
400This contract has not been sent for signature yet
400You must agree to use electronic records and signatures in order to sign
409The contract text changed after it was signed. This envelope can no longer be executed — void it and send a fresh one.
400You already signed this contract
400computed at runtime: {'message': f"Required field '{label}' on page {int(field.get('page') or 0) + 1} is not completed", 'field_id': str(field.get('id')), 'field_type': ftype, 'page': int(field.get('page') or 0)}
#post-api-v1-salesshift-contracts-sign-tokencontracts_router · vxcloud_fastapiclient/app/services/salesshift/contracts_router.py:720The contracts 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/contracts/sign/{token}/declineurl-secret-onlyVerified in sessionDecline to sign.

Decline to sign. Every comparable product has this; without it a counterparty who disagrees with the agreement has no action available but to close the tab, and the sender never learns why.

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

The signing token is the credential.

Path parameters

NameTypeRequiredNotes
tokenstringrequired

Request body (required) — DeclineIn

FieldTypeRequiredNotes
reasonstringrequired≤ 2000 chars.
namestringoptionalDefault "". ≤ 200 chars.

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

Request

curl
curl -X POST 'https://api.vxcloud.io/api/v1/salesshift/contracts/sign/{token}/decline' \
  -H 'Content-Type: application/json' \
  -d '{ "reason": "<string>", "name": "" }'

Response · 200

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

Errors

Statusdetail
400This link is for downloading the executed copy only
400Contract already {...}
400You already signed this contract
#post-api-v1-salesshift-contracts-sign-token-declinecontracts_router · vxcloud_fastapiclient/app/services/salesshift/contracts_router.py:978The contracts 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/contracts/sign/{token}/pdfurl-secret-onlyVerified in sessionExecuted-contract PDF, rendered by the tenant node's Go e-sign engine (document + signature frames + certificate of completion + audit trail).

Executed-contract PDF, rendered by the tenant node's Go e-sign engine (document + signature frames + certificate of completion + audit trail). Once the envelope is completed the bytes are frozen and served from the row, so the artifact a party downloads next year is the artifact they downloaded at signing.

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

The signing token is the credential.

Path parameters

NameTypeRequiredNotes
tokenstringrequired

Request

curl
curl 'https://api.vxcloud.io/api/v1/salesshift/contracts/sign/{token}/pdf'

Response · 200

Response carrying the executed PDF (application/pdf).

Errors

Statusdetail
502PDF engine unavailable: {...}
#get-api-v1-salesshift-contracts-sign-token-pdfcontracts_router · vxcloud_fastapiclient/app/services/salesshift/contracts_router.py:1185The contracts 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/contracts/uploadVerified in sessionBring-your-own contract: upload PDF / DOCX / TXT / MD / HTML — the content is extracted into an editable document that rides the full e-sign flow (edit → send → dual signatures → Go-rendered PDF).

Bring-your-own contract: upload PDF / DOCX / TXT / MD / HTML — the content is extracted into an editable document that rides the full e-sign flow (edit → send → dual signatures → Go-rendered PDF).

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

Request body (required) — Body_upload_contract_api_v1_salesshift_contracts_upload_post

FieldTypeRequiredNotes
filestringrequired
deal_idstringoptionalDefault "".
titlestringoptionalDefault "".
party_b_namestringoptionalDefault "".
party_b_emailstringoptionalDefault "".

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

Request

curl
curl -X POST 'https://api.vxcloud.io/api/v1/salesshift/contracts/upload' \
  -H "Authorization: Bearer $SS_TOKEN" \
  -H 'Content-Type: application/json' \
  -d '{ "file": "<string>", "deal_id": "", "title": "", "party_b_name": "", "party_b_email": "" }'

Response · 201

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

Errors

Statusdetail
400File too large (10 MB max)
400computed at runtime: str(exc)
400Could not read file: {...}
#post-api-v1-salesshift-contracts-uploadcontracts_router · vxcloud_fastapiclient/app/services/salesshift/contracts_router.py:1549The contracts 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.