Send a signing request
This walkthrough sends a program agreement to a parent signing for a child. Confirm the people and household first, choose the documents, preview, send, then check the result. Make every request from your backend using the Base URL and Bearer key from Getting started.
All examples use synthetic data, not customer records or captured production responses. Replace IDs with values returned for your organization. Before sending, check that you control the recipient's test inbox. Example template text illustrates the interface; it is not a legal contract.
Before you start
| Requirement | Check |
|---|---|
| Organization | GET /v1/me identifies the intended club |
| Scopes | club.contacts.read, club.contacts.read_pii, club.compliance.read, club.compliance.write |
| Parent, child and relationship | Already maintained in this club; the child has an accurate birthday and the parent a usable email |
| Template | The merchant has configured the program, schedule, price and companion-document rules, and published an available version |
| Signed files (optional) | club.compliance.read_files, needed only to check signed files in step 5 |
This walkthrough does not create people. Use the merchant console to complete profiles, or create and rotate to a new key carrying club.contacts.write and use the contact endpoints. Existing key permissions cannot be edited; see Scopes. See Field sources for ownership.
1. Identify the parent and child
Call GET /v1/club/contacts?search=Demo&page=1&pageSize=20. This is a household-oriented list: children may not have separate rows, and names/contact details may be masked. Do not take the first search result as the signer.
For a candidate parent, call GET /v1/club/contacts/{id} to verify the full profile, then GET /v1/club/contacts/{id}/households to inspect household members. Example for parent 3101:
{
"data": [
{
"id": "9900",
"name": "Demo household",
"members": [
{
"contactId": "3101",
"relation": "primary_guardian",
"firstName": "Alex",
"lastName": "Demo",
"birthday": "1985-05-06",
"roles": ["PARENT"]
},
{
"contactId": "3102",
"relation": "dependent",
"firstName": "Robin",
"lastName": "Demo",
"birthday": "2016-03-14",
"roles": ["PLAYER"]
}
]
}
]
}Parent 3101 signs; child 3102 is the participant the contract concerns. Household records help identify the relationship but do not automatically prove legal guardianship. Ask the merchant to resolve ambiguity; do not infer relationships from surnames or email addresses. Also retrieve the child's profile to confirm club membership and complete data.
An empty search can return:
{
"data": [],
"meta": {
"page": 1,
"pageSize": 20,
"total": 0
}
}This means no match for this query, not a permission error or proof that the person does not exist elsewhere on the platform. Refine the search or ask the merchant to check existing records before creating anyone.
2. Choose the agreement and companion documents
Call GET /v1/club/compliance/document-templates?documentKind=service_contract&active=true. Example:
{
"data": [
{
"id": "810",
"templateKey": "demo-program-agreement",
"title": "Demo program agreement",
"documentKind": "service_contract",
"ownerScope": "tenant_group",
"groupId": "123",
"active": true,
"currentVersion": 3,
"currentVersionId": "8103",
"effectiveFrom": "2026-09-01T00:00:00.000Z",
"aiSummary": null
}
]
}Read GET /v1/club/compliance/document-templates/{id} to check the title, field definitions and program configuration. For history, call GET /v1/club/compliance/document-templates/{id}/versions.
These are three different values: template ID 810, version number 3, and version ID 8103. Sending requires the version ID. If currentVersionId is null, ask the merchant to publish a version first.
Call POST /v1/club/compliance/signing-requests/resolve-waivers for the child. In this endpoint, contactId means the participant, not the parent who will receive the email:
{
"contactId": "3102",
"pdsaTemplateId": "810"
}Example HTTP 200 response:
{
"data": {
"memberIsMinor": true,
"dobMissing": false,
"appendixMatch": "matched",
"items": [
{
"waiverTemplateKey": "demo-risk-acknowledgment",
"templateVersionId": "8201",
"documentKind": "waiver",
"title": "Demo risk acknowledgment",
"applicability": "minorOnly",
"reason": "Selected by the demo template rule",
"defaultChecked": true
}
]
}
}defaultChecked is a rule-derived default, not proof of compliance. The merchant confirms the final document set; include the selected versions in items in the next step.
If dobMissing: true and memberIsMinor: null, do not classify the person as an adult. Complete the birthday and resolve again. An unchecked age-dependent item does not mean it is unnecessary. appendixMatch: "ambiguous" requires a person to confirm the sport-specific appendix.
3. Preview without sending
Call POST /v1/club/compliance/signing-requests/preview. No Idempotency-Key is required:
{
"contactId": "3101",
"dependentContactId": "3102",
"flowFlavor": "standard",
"items": [
{
"documentKind": "service_contract",
"templateVersionId": "8103",
"required": true,
"displayOrder": 0
},
{
"documentKind": "waiver",
"templateVersionId": "8201",
"required": true,
"displayOrder": 1
}
]
}Here contactId is the parent receiving the email and signing; dependentContactId is the child. An ordinary agreement signed on a child's behalf still uses flowFlavor: "standard". Do not choose parental_consent just because a child is involved: that branch has separate consent-record behavior.
A successful call returns HTTP 200 with one entry per document:
{
"data": [
{
"documentKind": "service_contract",
"templateVersionId": "8103",
"title": "Demo program agreement",
"renderedMarkdown": "# Demo program agreement\n\nClub: Demo Club\nProgram: Demo program\nParticipant: [to be completed at signing]\nSigned at: [recorded at signing]\n"
},
{
"documentKind": "waiver",
"templateVersionId": "8201",
"title": "Demo risk acknowledgment",
"renderedMarkdown": "# Demo risk acknowledgment\n\n…\n"
}
]
}Entries follow the order of the items you sent. Match them by templateVersionId rather than by position.
Check each renderedMarkdown. Check the club details, program, price, ordering and required items. A preview is not the final signed document. Signer fields can still contain [to be completed at signing], while audit fields can contain [recorded at signing]. Creation may also bundle electronic-signing consent.
This preview endpoint currently uses Markdown rendering. Do not expect a complete visual preview here for a template bound to a PDF/DOCX master. Review the document in the merchant signing flow; do not bypass review and send after a rendering error.
4. Create the request and send the invitation
Call POST /v1/club/compliance/signing-requests with Content-Type: application/json and a unique Idempotency-Key for this send, such as demo-signing-20260912-001.
{
"contactId": "3101",
"dependentContactId": "3102",
"flowFlavor": "standard",
"items": [
{
"documentKind": "service_contract",
"templateVersionId": "8103",
"required": true,
"displayOrder": 0
},
{
"documentKind": "waiver",
"templateVersionId": "8201",
"required": true,
"displayOrder": 1
}
],
"ttlHours": 72
}Example HTTP 201 response:
{
"data": {
"signingRequestId": "9200",
"status": "sent",
"expiresAt": "2026-09-15T09:00:00.000Z",
"consentBundled": false
}
}Store signingRequestId against your own business record. This signer already has current electronic-signing consent, so no item was added. A response with consentBundled: true means the current consent item was bundled or reused. Read the request back for its final item list.
sent means the request was initiated, not that the email was delivered. SportsX sends the invitation. The Partner API does not return the raw signing token; do not construct a signing link from the request ID. For timeouts or 409 responses, follow Write requests and retries. Do not switch idempotency keys to send again.
5. Check the signing result
Call GET /v1/club/compliance/signing-requests/{id} for the request you just created, 9200. For example, after the parent opens the link, the documents are still unfinished:
{
"data": {
"id": "9200",
"groupId": "123",
"contactId": "3101",
"status": "opened",
"expiresAt": "2026-09-15T09:00:00.000Z",
"sentAt": "2026-09-12T09:00:00.000Z",
"firstOpenedAt": "2026-09-12T09:10:00.000Z",
"completedAt": null,
"declinedAt": null,
"cancelledAt": null,
"flowFlavor": "standard",
"dependentContactId": "3102",
"signingTokenVersion": "1",
"createdByContactId": null,
"createdAt": "2026-09-12T09:00:00.000Z",
"updatedAt": "2026-09-12T09:10:00.000Z",
"items": [
{
"id": "9300",
"documentKind": "service_contract",
"templateVersionId": "8103",
"required": true,
"displayOrder": 0,
"itemStatus": "pending",
"confirmedAt": null,
"signedAt": null
},
{
"id": "9301",
"documentKind": "waiver",
"templateVersionId": "8201",
"required": true,
"displayOrder": 1,
"itemStatus": "pending",
"confirmedAt": null,
"signedAt": null
}
],
"signedDocuments": []
}
}opened only means the link was opened. Do not mark the agreement signed in your system. Here completedAt is null, the document items remain unsigned, and signedDocuments is empty.
| Status | What to do |
|---|---|
draft, sent, opened, in_progress | Not complete; poll infrequently with a bounded retry policy and respect rate limits |
partially_completed | Do not mark everything complete; inspect item results and ask the merchant to review |
completed | Check completion time and per-item evidence before updating your business record |
declined, cancelled, expired | Retain the result and let the merchant decide next steps; do not automatically resend |
Use GET /v1/club/compliance/signing-requests/{id}/events to inspect viewing and signing events.
Retrieving signed files needs the additional club.compliance.read_files scope (existing key permissions cannot be edited, so create and rotate to a new key). Once the request is completed, read it again: each entry under signedDocuments carries a finalFileAssetId, which is the signed file. A completion certificate, when present, is a separate completionCertificateFileAssetId and can be null. Exchange that id for a URL:
{
"data": {
"url": "https://files.example.com/demo-signed.pdf?sig=…",
"expiresAt": "2026-09-12T10:00:00.000Z"
}
}The url expires. Do not store it as a permanent link — request a fresh one when you need the file. Every exchange is audited.
Acceptance should include a controlled inbox receiving an invitation, the parent opening and signing each item, querying completion, and checking the signed files. Correct API responses alone do not verify field values, email delivery, final files or evidence.