Skip to content
Last updated

Working with debt collection endpoints

This guide covers the full lifecycle of a debt collection integration with the Open API: from receiving a run, through reporting payments and write-offs, to closing cases.

Relevant endpoints

How it works

A debt collection run is a batch of overdue customer claims that a studio hands over to a debt collection partner. The studio creates and verifies the run in , which triggers a notification to the partner via webhook.

The integration follows four steps:

  1. Receive — Studio verifies the run. Partner receives a webhook and fetches the debtor list.
  2. Confirm — Partner confirms receipt of the cases.
  3. Update — Partner sends case updates as collection progresses: payments, write-offs, partial reductions, blocks.
  4. Close — Partner closes the case when collection is complete or unsuccessful.

To get started with registration and activation, refer to Managing Activations.

Authentication and access

Scopes

Scopes define what the partner is allowed to do. The following scopes are required for all debt collection integrations:

Scopes are configured per partner application in the Developer Portal. If a required scope is missing, the request is rejected before any business logic runs.

Partners can optionally be granted read access to individual customer records for the debtors in an active run.

ScopeAccess
CUSTOMER_READCustomer profile
CUSTOMER_DOCUMENT_READCustomer documents
CUSTOMER_CONTRACT_READCustomer contracts
CUSTOMER_PAYMENT_SEARCHPayment history

Per-customer data privacy grants

Even with the correct scopes, a partner cannot access any customer's data by default. Access is granted per customer, automatically, when a debt collection run is transferred.

  • Grant: When the studio confirms a run, writes a data privacy grant for each customer in the run, scoped to the partner's API key. This allows the partner to call customer-scoped endpoints (e.g. GET /v1/customers/{id}/documents) for those specific customers only.
  • Revocation: When a case is closed (POSITIVE, NEGATIVE, or REVERSAL) and the customer has no other open debt collection cases with the same partner, the grant is automatically removed.
  • Multiple concurrent runs: If a customer appears in multiple active runs for the same partner, the grant stays active until the last open case is closed.

The partner never has blanket access to the studio's full customer base — only to the specific debtors actively in collection with them.

Key concepts

IDs you need to track

Every debt collection case involves three IDs. Keep all three — you need them for every update.

IDScopeWhat it is
debtorIdPer studio (not globally unique)Identifies the member within a specific studio's debt collection context
collectionCaseIdPer transferIdentifies a debtor's case within a single debt collection run. One debtor can have multiple IDs if they appeared in multiple runs
debtIdPer debtIdentifies a single overdue charge. Required for amount-level reporting

Updates always replace — they are not deltas

Every call to POST Update debt collection sends the complete current state of the case. reads the new values and reconciles them with the previous state automatically.

This means:

  • Always include all collectionCaseIds for the partner's case, even if only one changed
  • Always include all debts, even those with no changes in this update
  • Never send only the fields that changed — send the full case every time

The agencyCollectionCaseId field

The agencyCollectionCaseId is the partner's own case identifier. It is technically optional in the API, but must be set on every update for two critical reasons.

Grouping across multiple runs

A debtor can appear in more than one debt collection run (for example, the studio submits a second run for the same member after the first one was already transferred). The agencyCollectionCaseId is what ties all of those collectionCaseIds together under a single partner case. and the studio see all matching case IDs as one unit of work. Without it, has no way to associate the cases, and the studio cannot tell which collectionCaseIds belong together.

Invoicing

The partner invoices the studio for collection work per case. Only the first collectionCaseId under a given agencyCollectionCaseId is billed. Any additional collectionCaseIds grouped under the same ID are treated as additions to the same case and are not billed separately.

If agencyCollectionCaseId is not set, each collectionCaseId is treated as a separate case — the studio will be over-billed for what is in reality one collection engagement.

Set agencyCollectionCaseId on the very first update for a debtor and keep it consistent across all subsequent updates for that case.

How debt amounts work

Each debt has four amount fields that together define its current state:

FieldWhat it means
originalAmountThe total amount transferred to the partner for collection
paidAmountRunning total received from the debtor
reducedAmountRunning total of negotiated write-downs or partial waivers
writeOffAmountRunning total formally derecognized for tax/accounting purposes

The open amount in the studio's books is calculated as:

openAmount = originalAmount - paidAmount - reducedAmount - writeOffAmount

All four fields are cumulative totals, not deltas. Always send the running total. To increase a write-off from 20 to 50, send writeOffAmount: 50. To fully reverse it, send writeOffAmount: 0.

Step 1 — Receive a debt collection run

When a studio verifies a debt collection run, fires a FINANCE_DEBT_COLLECTION_RUN_CREATED webhook (see Event types). The event body contains an entityId field holding the debtCollectionRunId.

Use this ID to fetch the case details:

  • GET Debtors — Returns all debtors for the run in pages. Use this to get debtorId, collectionCaseId, and debtId values for each debtor.
  • GET Transfer Details — Returns metadata about the transfer: status, date, client information.

Note: debtorId is scoped per studio, not globally unique. The same physical person at two different studios will have different debtorId values.

Step 2 — Confirm retrieval

After fetching the debtor list, the partner must confirm receipt using POST Confirm transfer retrieval. This tells the studio that the data was successfully transferred to the partner.

Confirm immediately after fetching the cases. Do not wait until collection begins.

Step 3 — Report updates

Reporting payments

Use POST Update debt collection to report any state change as collection progresses. Increase paidAmount as payments come in. See the examples section for full payloads.

Set agencyCollectionCaseId on every update — see The agencyCollectionCaseId field for why this is critical for grouping and invoicing. will include it in webhook events sent back to you, so you can correlate incoming events with your own records. The optional publicCollectionCaseId is a separate ID used in debtor-facing communication.

Reporting write-offs and reductions

Partners can reduce the open amount on individual debts without closing the case. This is useful for tax write-offs, negotiated partial waivers, or temporary derecognition during insolvency proceedings.

Use the writeOffAmount and reducedAmount fields on each debt object:

FieldBooking entry in Use for
writeOffAmountDebtClaimAbandonmentFormal tax derecognition requested by the studio
reducedAmountDebtClaimReductionNegotiated write-down or partial waiver

Prerequisites — both must be enabled:

  1. Partner-level flag (Developer Portal): The debt modification capability must be enabled for the integration in the Developer Portal. Once enabled at the partner level, it is automatically forwarded to the studio tenants connected to that partner. Contact Sport Alliance to enable this for your integration.

  2. Studio-level setting: Each studio must enable immediate debt modification under Settings -> Finance -> Collection. On the Collection Partner configuration, the "Debt modification on update" option must be set to Immediately. If set to IGNORED (the default), values are stored on the case but no booking entries are created in the studio's member account.

Verify the current mode via the configuration endpoint — look for debtModificationOnUpdateMode: IMMEDIATELY.

Key behaviors:

  • The member's access restriction and debt collection status are not removed when the open amount reaches 0 via write-off. Only a closure lifts the restriction.
  • A debt written off to 0 will not appear in future debt collection runs.
  • Write-offs and reductions are fully reversible while the case is open — see the write-off examples.

Note on canceledAmount: This field is deprecated. Use writeOffAmount instead. Existing canceledAmount values are automatically migrated to writeOffAmount when the studio enables IMMEDIATELY mode.

Blocking debtors and debts

A debtor or an individual debt can be excluded from future debt collection runs by adding a block property to the update. Removing the block property in a follow-up update removes the block.

See the blocking examples for full payloads.

Step 4 — Close a case

Send a closure object inside the case update to close a case. The closure type determines what happens in :

TypeEffect on the member
POSITIVEDebt collection status removed. Member access restored (if no other open cases).
NEGATIVEDebt collection status removed. Same as POSITIVE from the member's perspective.
REVERSALDebt collection status removed. Use when the partner terminates the contract.
REJECTIONDebt collection status stays. All restrictions remain. rejectionReason is required.

The WRITE_OFF_REMAINING_DEBTS option can be added to POSITIVE or NEGATIVE closures to write off any remaining open amount at closure time. Alternatively, use per-debt writeOffAmount before closing — both approaches can be combined.

Closure is final. Once a case is closed, it cannot be reopened via a subsequent update. A follow-up update without a closure element does not reopen the case. The same applies to WRITE_OFF_REMAINING_DEBTS: if the first closure update does not include it, a follow-up update adding it will be ignored.

After closure, the partner can still report payments by increasing paidAmount. will create the corresponding payment booking in the member account.

Handling studio-initiated updates

Studios can update case status directly in the UI (for example, closing or reverting a case) without contacting the partner first. When this happens, fires a webhook so the partner's records stay in sync.

Permissions

This feature must be explicitly enabled for the partner in the Developer Portal before studio-side updates trigger webhook events.

Webhook event

The event payload contains the caseAdjustmentRequestId as entityId, plus either:

  • agencyCollectionCaseId (if the partner has assigned one), or
  • collectionCaseId

Partner action

On receiving the webhook, fetch the updated case state via GET debt collection cases using the ID from the event, then update your records accordingly.

Special situations

Home studio changes

When a studio moves a customer to a different home studio, partners must listen to the CUSTOMER_HOME_STUDIO_UPDATED webhook event (sometimes shown as customer_home_studio_updated). The event includes content.sourceStudioId and content.targetStudioId.

After a home studio change, use the Open API Token for content.targetStudioId for all future retrievals and updates for that customer. Using the old studio's token will result in errors.

Contract termination

Studio switches to a new partner: The old partner continues handling existing cases as normal but receives no new runs.

Full termination (either side initiates):

  • If initiated by the studio: close all cases with type: REJECTION and rejectionReason: WITHDRAWN_BY_STUDIO.
  • If initiated by the partner: close all cases with type: REVERSAL.

Customer pays directly to the studio

Automatic payment forwarding from studio to partner is not supported. If a customer pays the studio directly:

  1. The studio informs the partner out of band.
  2. The partner reports the payment via Open API as if received directly (increase paidAmount).
  3. The studio does not book the payment in .

Money transfer arrangements between studio and partner are at their own discretion.

Adjustments of debts by the studio

There are two paths, depending on what needs to change:

Partner reports a write-off or reduction (preferred): If the studio has agreed to a write-off or partial reduction, apply it directly via writeOffAmount or reducedAmount in the next case update. No closure required. See Reporting write-offs and reductions for prerequisites.

Studio needs to change the debt structure (wrong amount, missing debt): If the original amount was incorrect or a debt needs to be split, writeOffAmount and reducedAmount are not sufficient. The only path is:

  1. Reject the case with rejectionReason: WITHDRAWN_BY_STUDIO (see Reversal of Case)
  2. The studio adjusts the debt in
  3. The studio creates a new debt collection run

(A feature currently in development will streamline this: the studio will be able to adjust a debt mid-collection, and the partner will receive a notification with the option to accept or reject the change.)

Examples

The following examples show the exact payloads to send for common scenarios. All examples share the same preconditions.

Preconditions

Each example assumes:

  • The studio has performed 2 debt collection runs, and you as partner have fetched the included member
  • Both debt collection runs contained exactly one member, with debtorId debtorid (usually a UUID or number — simplified here for readability)
  • The first debt collection run:
  • The second debt collection run:

Single payment with case closure

(See preconditions)

Lets assume you have received a single payment on the full amount and want to report the payment with a successful case closure.

You would send the following update to POST Update debt collection:

{
  "debtors": [
    {
      "debtorId": "debtorid",
      "agencyCollectionCases": [
        {
          "agencyCollectionCaseId": "your-case-id",
          "publicCollectionCaseId": "optional-your-customer-facing-id",
          "collectionCaseIds": [
            "collection-caseid-1",
            "collection-caseid-2"
          ],
          "closure": {
            "type": "POSITIVE",
            "options": [],
            "date": "2024-10-14",
            "closureReason": "Optional, e.g. 'Member has fully paid'"
          },
          "debts": [
            {
              "debtId": "debtid-1",
              "originalAmount": 10,
              "paidAmount": 10,
              "reducedAmount": 0,
              "writeOffAmount": 0,
              "currency": "EUR"
            },
            {
              "debtId": "debtid-2",
              "originalAmount": 20,
              "paidAmount": 20,
              "reducedAmount": 0,
              "writeOffAmount": 0,
              "currency": "EUR"
            }
          ]
        }
      ]
    }
  ],
  "requestId": "your-request-id"
}

Consequences

  • will create a payment of 30 EUR (since this is your first update and the paidAmount is 30 EUR in total)
  • The debt collection case is closed in
  • Assuming that the Member has no other debt collection cases or open debts, the debt collection level is removed from the member and he may enter the Gym again

Partial payment with case closure

(See preconditions)

There was a negotiation with the debtor, and you and the debtor agreed upon that he must only pay 50% of the total value to close the case.

You received the money and want to report to the payment of 50% (15 EUR) and close the case.

Its up to you how the partial payment is distributed among the open debts. In this example we assume that the paid amount is distributed equally among the debts.

You would send the following update to POST Update debt collection:

{
  "debtors": [
    {
      "debtorId": "debtorid",
      "agencyCollectionCases": [
        {
          "agencyCollectionCaseId": "your-case-id",
          "publicCollectionCaseId": "optional-your-customer-facing-id",
          "collectionCaseIds": [
            "collection-caseid-1",
            "collection-caseid-2"
          ],
          "closure": {
            "type": "POSITIVE",
            "options": [],
            "date": "2024-10-14",
            "closureReason": "Optional, e.g. 'Negotiated case closure with partial payment'"
          },
          "debts": [
            {
              "debtId": "debtid-1",
              "originalAmount": 10,
              "paidAmount": 5,
              "reducedAmount": 0,
              "writeOffAmount": 0,
              "currency": "EUR"
            },
            {
              "debtId": "debtid-2",
              "originalAmount": 20,
              "paidAmount": 10,
              "reducedAmount": 0,
              "writeOffAmount": 0,
              "currency": "EUR"
            }
          ]
        }
      ]
    }
  ],
  "requestId": "your-request-id"
}

Consequences

  • will create a payment of 15 EUR (since this is your first update and the paidAmount is 15 EUR in total)
  • The debt collection case is closed in
  • If the studio has a setting enabled to write off remaining debts, the 2 debts will be reduced by the still open amount, so the debtor then has no open debts anymore
    • If the remaining debts are written off the debt collection level of the debtor is removed and he may enter the Gym again, if he is still a member with active contract
  • Alternatively, partners can report a per-debt writeOffAmount for the still-open portion in the same update (or before sending the closure) instead of relying on the studio-side setting. See Reporting write-offs and reductions for prerequisites.

Intermediate payment

(See preconditions)

You received a partial payment of 5 EUR by the debtor. You assign this payment to one debt (debtid) and want to report this payment to , without closing the case.

You would send the following update to POST Update debt collection:

{
  "debtors": [
    {
      "debtorId": "debtorid",
      "agencyCollectionCases": [
        {
          "agencyCollectionCaseId": "your-case-id",
          "publicCollectionCaseId": "optional-your-customer-facing-id",
          "collectionCaseIds": [
            "collection-caseid-1",
            "collection-caseid-2"
          ],
          "debts": [
            {
              "debtId": "debtid-1",
              "originalAmount": 10,
              "paidAmount": 5,
              "reducedAmount": 0,
              "writeOffAmount": 0,
              "currency": "EUR"
            },
            {
              "debtId": "debtid-2",
              "originalAmount": 20,
              "paidAmount": 0,
              "reducedAmount": 0,
              "writeOffAmount": 0,
              "currency": "EUR"
            }
          ]
        }
      ]
    }
  ],
  "requestId": "your-request-id"
}

Consequences

  • will create a payment of 5 EUR

Note that as in every update, both collectionCaseIds are added, and also both debts, also the one that didn't change. The update always must contain the whole case, including all case ids and debts.

Reversal of payment

(See preconditions)

Lets assume you first reported by mistake a payment of 5 EUR on debt debtid-1. Actually you only received 2 EUR by the debtor and want to correct this.

You would send the following update to POST Update debt collection:

{
  "debtors": [
    {
      "debtorId": "debtorid",
      "agencyCollectionCases": [
        {
          "agencyCollectionCaseId": "your-case-id",
          "publicCollectionCaseId": "optional-your-customer-facing-id",
          "collectionCaseIds": [
            "collection-caseid-1",
            "collection-caseid-2"
          ],
          "debts": [
            {
              "debtId": "debtid-1",
              "originalAmount": 10,
              "paidAmount": 2,
              "reducedAmount": 0,
              "writeOffAmount": 0,
              "currency": "EUR"
            },
            {
              "debtId": "debtid-2",
              "originalAmount": 20,
              "paidAmount": 0,
              "reducedAmount": 0,
              "writeOffAmount": 0,
              "currency": "EUR"
            }
          ]
        }
      ]
    }
  ],
  "requestId": "your-request-id"
}

Consequences

  • will reduce the previously created payment of 5 EUR down to 2 EUR

Reversal of Case

(See preconditions)

Lets assume the studio you are working with has submitted a member to debt collection by mistake. The studio sends and email to you, asking to close the case without any processing.

You would send the following update to POST Update debt collection:

{
  "debtors": [
    {
      "debtorId": "debtorid",
      "agencyCollectionCases": [
        {
          "agencyCollectionCaseId": "your-case-id",
          "publicCollectionCaseId": "optional-your-customer-facing-id",
          "collectionCaseIds": [
            "collection-caseid-1",
            "collection-caseid-2"
          ],
          "closure": {
            "type": "REJECTION",
            "rejectionReason": "WITHDRAWN_BY_STUDIO",
            "options": [],
            "date": "2024-10-14",
            "closureReason": "Optional, e.g. 'Studio asked for case closure on 2024-10-14 as transfer made by mistake'"
          },
          "debts": [
            {
              "debtId": "debtid-1",
              "originalAmount": 10,
              "paidAmount": 0,
              "reducedAmount": 0,
              "writeOffAmount": 0,
              "currency": "EUR"
            },
            {
              "debtId": "debtid-2",
              "originalAmount": 20,
              "paidAmount": 0,
              "reducedAmount": 0,
              "writeOffAmount": 0,
              "currency": "EUR"
            }
          ]
        }
      ]
    }
  ],
  "requestId": "your-request-id"
}

Rejection by Partner

(See preconditions)

Lets assume your received a debtor but with the given address information it is not possible to contact the debtor, and any address search was also unsuccessuful.

You would send the following update to POST Update debt collection:

{
  "debtors": [
    {
      "debtorId": "debtorid",
      "agencyCollectionCases": [
        {
          "agencyCollectionCaseId": "your-case-id",
          "publicCollectionCaseId": "optional-your-customer-facing-id",
          "collectionCaseIds": [
            "collection-caseid-1",
            "collection-caseid-2"
          ],
          "closure": {
            "type": "REJECTION",
            "rejectionReason": "POSTAL_DELIVERY_NOT_POSSIBLE",
            "options": [],
            "date": "2024-10-14",
            "closureReason": "Optional: "
          },
          "debts": [
            {
              "debtId": "debtid-1",
              "originalAmount": 10,
              "paidAmount": 0,
              "reducedAmount": 0,
              "writeOffAmount": 0,
              "currency": "EUR"
            },
            {
              "debtId": "debtid-2",
              "originalAmount": 20,
              "paidAmount": 0,
              "reducedAmount": 0,
              "writeOffAmount": 0,
              "currency": "EUR"
            }
          ]
        }
      ]
    }
  ],
  "requestId": "your-request-id"
}

Blocking and Unblocking Debt Collection Cases

(See preconditions)

Let's assume you want to block the debtor from future debt collection runs, you have sent an update with a block for the debtor.

You would send the following update to POST Update debt collection:

{
  "debtors": [
    {
      "debtorId": "debtorid",
      "agencyCollectionCases": [
        {
          "agencyCollectionCaseId": "your-case-id",
          "publicCollectionCaseId": "optional-your-customer-facing-id",
          "collectionCaseIds": [
            "collection-caseid-1",
            "collection-caseid-2"
          ],
          "block": {
            "limitType": "LIMITED",
            "endDate": "2024-09-20"
          },
          "debts": [
            {
              "debtId": "debtid-1",
              "originalAmount": 10,
              "paidAmount": 0,
              "reducedAmount": 0,
              "writeOffAmount": 0,
              "currency": "EUR"
            },
            {
              "debtId": "debtid-2",
              "originalAmount": 20,
              "paidAmount": 0,
              "reducedAmount": 0,
              "writeOffAmount": 0,
              "currency": "EUR"
            }
          ]
        }
      ]
    }
  ],
  "requestId": "your-request-id"
}

Because there are no blocks in the json at the specific debts, eventually existing blocks will be removed.

If you later on want to enable the debtor but block debtid-2 forever, you would send the following update to POST Update debt collection:

{
  "debtors": [
    {
      "debtorId": "debtorid",
      "agencyCollectionCases": [
        {
          "agencyCollectionCaseId": "your-case-id",
          "publicCollectionCaseId": "optional-your-customer-facing-id",
          "collectionCaseIds": [
            "collection-caseid-1",
            "collection-caseid-2"
          ],
          "debts": [
            {
              "debtId": "debtid-1",
              "originalAmount": 10,
              "paidAmount": 0,
              "reducedAmount": 0,
              "writeOffAmount": 0,
              "currency": "EUR"
            },
            {
              "debtId": "debtid-2",
              "originalAmount": 20,
              "paidAmount": 0,
              "reducedAmount": 0,
              "writeOffAmount": 0,
              "currency": "EUR",
              "block": {
                "limitType": "UNLIMITED"
              }
            }
          ]
        }
      ]
    }
  ],
  "requestId": "your-request-id"
}

Write-off while keeping case open

(See preconditions)

The studio requests that the full outstanding amount on debtid-1 (10 EUR) be written off for tax reasons. The case should remain open for continued collection on debtid-2.

You would send the following update to POST Update debt collection:

{
  "debtors": [
    {
      "debtorId": "debtorid",
      "agencyCollectionCases": [
        {
          "agencyCollectionCaseId": "your-case-id",
          "collectionCaseIds": [
            "collection-caseid-1",
            "collection-caseid-2"
          ],
          "debts": [
            {
              "debtId": "debtid-1",
              "originalAmount": 10,
              "paidAmount": 0,
              "reducedAmount": 0,
              "writeOffAmount": 10,
              "currency": "EUR"
            },
            {
              "debtId": "debtid-2",
              "originalAmount": 20,
              "paidAmount": 0,
              "reducedAmount": 0,
              "writeOffAmount": 0,
              "currency": "EUR"
            }
          ]
        }
      ]
    }
  ],
  "requestId": "your-request-id"
}

Consequences:

  • creates a DebtClaimAbandonment booking of -10 EUR on debtid-1
  • Open amount of debtid-1: 10 - 0 (paid) - 0 (reduced) - 10 (written off) = 0 EUR
  • debtid-1 will not appear in future debt collection runs
  • The debt collection case remains open; debtid-2 is unaffected
  • The member's debt collection status and entrance restriction are unchanged

Partial reversal of a write-off after a payment

(See preconditions)

Following the previous example (writeOffAmount: 10 on debtid-1), a payment of 5 EUR is received. Reduce the write-off by the same amount — both fields are cumulative totals, so send the new running totals.

{
  "debtors": [
    {
      "debtorId": "debtorid",
      "agencyCollectionCases": [
        {
          "agencyCollectionCaseId": "your-case-id",
          "collectionCaseIds": [
            "collection-caseid-1",
            "collection-caseid-2"
          ],
          "debts": [
            {
              "debtId": "debtid-1",
              "originalAmount": 10,
              "paidAmount": 5,
              "reducedAmount": 0,
              "writeOffAmount": 5,
              "currency": "EUR"
            },
            {
              "debtId": "debtid-2",
              "originalAmount": 20,
              "paidAmount": 0,
              "reducedAmount": 0,
              "writeOffAmount": 0,
              "currency": "EUR"
            }
          ]
        }
      ]
    }
  ],
  "requestId": "your-request-id"
}

Consequences:

  • The previous DebtClaimAbandonment of -10 EUR is partially reversed; a new abandonment of -5 EUR replaces it
  • A DebtCollectionPayment of 5 EUR is created
  • Open amount on debtid-1: 10 - 5 (paid) - 0 (reduced) - 5 (written off) = 0 EUR

This is a planned feature and currently not supported.

Payment within the studio (Planned)

This is a planned feature and currently not supported.

Send activity updates to the studio (Planned)

This is a planned feature and currently not supported.