Online Subscription Cancellation
Online cancellation allows a member to cancel their subscription autonomously, via your application or portal, without any intervention from the club.
Why is online cancellation mandatory?
Since June 2023, French law (known as the "3-click cancellation" law) requires any professional offering subscriptions to allow customers to cancel online, through the same channel used to subscribe. This obligation is being progressively extended to other European countries.
As a partner, if you offer online subscription via the Resamania API, you must also offer online cancellation. This document describes the endpoints available to implement it.
Club-side configuration
Online cancellation must be enabled by the club in its settings (Settings > Clubs / Legal > Cancellation tab > "Allow online subscription cancellation"). Without this activation, calls to POST /online_cancellation will be rejected.
Macro-process
Step 1 — Retrieve cancellation motives
Cancellation motives are configured by the club. Only motives explicitly included in the club's onlineCancellation configuration are accepted for online cancellation.
GET /{clientToken}/referentials/cancellation_motives
curl --location 'https://{{baseUrl}}/{{clientToken}}/referentials/cancellation_motives' \
--header 'accept: application/ld+json' \
--header 'authorization: Bearer {{bearer}}' \
--header 'x-user-club-id: {{clubId}}'{
"@context": "/demoapi/contexts/CancellationMotive",
"@id": "/demoapi/referentials/cancellation_motives",
"@type": "hydra:Collection",
"hydra:member": [
{
"@id": "/demoapi/referentials/cancellation_motives/1",
"@type": "CancellationMotive",
"id": 1,
"name": "Relocation"
},
{
"@id": "/demoapi/referentials/cancellation_motives/2",
"@type": "CancellationMotive",
"id": 2,
"name": "Medical reasons"
}
],
"hydra:totalItems": 2
}WARNING
Only present to the user the motives configured for online cancellation by the club (field includedCancellationMotives in the club's onlineCancellation configuration). Using an unauthorized motive will result in an api.error.subscription-cancel-online.motive-id-not-include error.
Step 2 — Retrieve available cancellation dates
Before presenting a date picker to the user, retrieve the authorized cancellation dates for the subscription. In online cancellation mode, cancellation always occurs on a renewal date (end of a full billing period), never on a free date.
GET /{clientToken}/subscriptions/{id}/cancellation_dates
| Parameter | Type | Description | Example |
|---|---|---|---|
receptionDate | string (query, optional) | Date the request was received. Defaults to today. | 2024-06-01 |
restrict | boolean (query, optional) | Restricts dates to renewal dates. Defaults to true. | true |
curl --location 'https://{{baseUrl}}/{{clientToken}}/subscriptions/{{subscriptionId}}/cancellation_dates?receptionDate=2024-06-01' \
--header 'accept: application/json' \
--header 'authorization: Bearer {{bearer}}' \
--header 'x-user-club-id: {{clubId}}'{
"dates": [
{ "date": "2024-07-01" },
{ "date": "2024-08-01" },
{ "date": "2024-09-01" }
]
}INFO
A 204 No Content response means no cancellation date is available (e.g. fixed-term subscription not yet expired, subscription already cancelled).
Step 3 — Create the cancellation
Two endpoints are available depending on the origin of the request.
Option A — POST /subscriptions/{id}/online_cancellation (recommended for the member area)
This endpoint is recommended for cancellations initiated by the member. It verifies all rules configured by the club (feature activation, authorized motive, debt limit, contact tags, minimum delay, daily quota) before creating the cancellation.
The cancellation is created and immediately accepted in a single operation, with inputChannel: member.
POST /{clientToken}/subscriptions/{id}/online_cancellation
| Parameter | Type | Required | Description |
|---|---|---|---|
cancellationDate | string (ISO date) | ✅ | Effective cancellation date. Must match a date returned by GET /cancellation_dates. |
cancellationMotiveId | string (IRI) | ✅ | IRI of the cancellation motive. Must be in the club's authorized motive list. |
comment | string | ❌ | Free-text comment from the member. |
curl --location --request POST 'https://{{baseUrl}}/{{clientToken}}/subscriptions/{{subscriptionId}}/online_cancellation' \
--header 'accept: application/ld+json' \
--header 'authorization: Bearer {{bearer}}' \
--header 'x-user-club-id: {{clubId}}' \
--header 'Content-Type: application/json' \
--data '{
"cancellationDate": "2024-07-01",
"cancellationMotiveId": "/demoapi/referentials/cancellation_motives/1",
"comment": "I am moving abroad."
}'{
"@context": "/demoapi/contexts/Subscription",
"@id": "/demoapi/subscriptions/42",
"@type": "Subscription",
"id": 42,
"cancellations": [
"/demoapi/cancellations/99"
]
}Error codes
| HTTP code | Error message | Cause |
|---|---|---|
400 | api.error.subscription-cancel-online.missing-date | The cancellationDate field is missing from the request body. |
400 | api.error.subscription-cancel-online.missing-motive-id | The cancellationMotiveId field is missing from the request body. |
400 | api.error.subscription-cancel-online.invalid-date | The cancellationDate format is invalid. |
400 | api.error.subscription-cancel-online.already-canceled | The subscription is already cancelled. |
400 | api.error.subscription-cancel-online.invalid-cancel-date | The date is in the past, before the end of the commitment period, or does not respect the configured notice period. |
400 | api.error.subscription-cancel-online.cancellation-date-invalid | The date is not among the authorized dates (not returned by GET /cancellation_dates). |
400 | api.error.subscription-cancel-online.subscription-contact-different-club | The subscription and the contact belong to different clubs. |
400 | api.error.subscription-cancel-online.not-enabled | Online cancellation is not enabled on this club for this subscription type. |
400 | api.error.subscription-cancel-online.motive-id-not-include | The provided motive is not in the list of motives authorized for online cancellation. |
400 | api.error.subscription-cancel-online.contact-tag-not-eligible | The contact does not have the required tags to cancel online. |
400 | api.error.subscription-cancel-online.contact-tag-prohibited | The contact has a tag that excludes them from online cancellation. |
400 | api.error.subscription-cancel-online.contact-debt-over-limit | The contact's debt exceeds the club's authorized limit. |
409 | (Conflict) | A cancellation is already being processed for this subscription (concurrent lock). |
Option B — POST /cancellations (back-office or admin)
This endpoint creates a cancellation directly without verifying online cancellation rules. It is suited for back-office cases (cancellation entered by a club employee from your interface) or for imports.
The inputChannel field identifies the origin of the request:
admin: cancellation entered by an employee from the back-office or your partner application.member: cancellation initiated by the member (preferPOST /online_cancellationin this case).
Two-step workflow
Unlike POST /online_cancellation, a cancellation created here has the status submitted. It must then be accepted via POST /{clientToken}/cancellations/{id}/transitions with { "transition": "accept" } to take effect on the subscription.
POST /{clientToken}/cancellations
| Parameter | Type | Required | Description |
|---|---|---|---|
subscription | string (IRI) | ✅* | IRI of the subscription to cancel. Mutually exclusive with subscriptionOption. |
subscriptionOption | string (IRI) | ✅* | IRI of the option to cancel. Mutually exclusive with subscription. |
receptionDate | string (ISO date) | ✅ | Date the cancellation request was received. |
cancellationDate | string (ISO date) | ✅ | Effective cancellation date. |
cancellationMotiveId | string (IRI) | ✅ | IRI of the cancellation motive. |
inputChannel | string | ✅ | Origin channel: admin or member. |
comment | string | ❌ | Internal comment. |
refundPolicy | string | ❌ | Refund policy: period_end (default), period_start, prorata. |
createCreditNote | boolean | ❌ | Generate a credit note. Defaults to true. |
* One of the two is required, but not both simultaneously.
curl --location --request POST 'https://{{baseUrl}}/{{clientToken}}/cancellations' \
--header 'accept: application/ld+json' \
--header 'authorization: Bearer {{bearer}}' \
--header 'x-user-club-id: {{clubId}}' \
--header 'Content-Type: application/json' \
--data '{
"subscription": "/demoapi/subscriptions/42",
"receptionDate": "2024-06-01",
"cancellationDate": "2024-07-01",
"cancellationMotiveId": "/demoapi/referentials/cancellation_motives/1",
"comment": "Cancellation requested by the member",
"inputChannel": "admin"
}'{
"@context": "/demoapi/contexts/Cancellation",
"@id": "/demoapi/cancellations/99",
"@type": "Cancellation",
"subscription": "/demoapi/subscriptions/42",
"subscriptionOption": null,
"receptionDate": "2024-06-01",
"cancellationDate": "2024-07-01",
"cancellationMotiveId": "/demoapi/referentials/cancellation_motives/1",
"comment": "Cancellation requested by the member",
"status": "submitted",
"inputChannel": "admin",
"type": "simple",
"contact": {
"@id": "/demoapi/contacts/123",
"familyName": "Doe",
"givenName": "John",
"number": "C123"
}
}Cancellation statuses
| Status | Description |
|---|---|
submitted | Cancellation created, pending validation. |
accepted | Cancellation accepted — the subscription will stop on the cancellationDate. |
rejected | Cancellation rejected. |
canceled | Cancellation voided. |
Important notes
Fixed-term and upfront subscriptions
Fixed-term (CDD) or upfront-payment subscriptions cannot be cancelled before their end date via online cancellation.
Linked options
Cancelling a main subscription automatically cancels its linked options. Mandatory options and options belonging to a package cannot be cancelled independently.
Multi-club networks
In a network, a contact attached to club A cannot cancel online a subscription taken out in club B.