Aksal Black Partner APIs
These endpoints allow Aksal to retrieve Aksal Black customer information: discount tier, active program challenges, and purchase history.
Authentication
- Uses signature authentication
| Header | Description |
|---|---|
X-SIGNATURE | The signature header of the request |
See 2-security.md for signature generation details.
Base path
/api/v1/partner/aksal/
Customer identifier
All endpoints below use the same path parameter, identifier, to look up an enrolled Aksal Black customer. It accepts either:
| Value type | Description | Example |
|---|---|---|
| Aksal Black card number | The customer's Aksal Black card number | AKSAL-BLACK-0001 |
| Aksal Black ID | The customer's Aksal Black ID, as returned by the enrollment endpoint | BLK2000001 |
| Mobile phone number | The customer's mobile number on file | 0611223344, +212611223344, or 212611223344 |
Spaces in the value are ignored. For phone numbers, local Moroccan formats starting with 0 are normalized to the international +212 format before lookup.
Endpoints
1) Black card tier
Retrieve the discount tier and customer summary for an Aksal Black customer.
GET /api/v1/partner/aksal/black-card/<identifier>/tier
| Parameter | Type | Description |
|---|---|---|
identifier | string | The Aksal Black card number, the Aksal Black ID, or the customer's mobile phone number (see above) |
Sample request (card number)
GET https://app.kenzup.com/api/v1/partner/aksal/black-card/AKSAL-BLACK-0001/tier
Sample request (mobile phone number)
GET https://app.kenzup.com/api/v1/partner/aksal/black-card/0611223344/tier
Status Code: 200
{
"tier_code": "aksal_gold_tier",
"first_name": "Ahmed",
"last_name": "Benali",
"customer_phone_number": "+212611223344",
"balance": "250.50",
"discount_pct": "15.00",
"label_fr": "Tier Or",
"label_ar": "الدرجة الذهبية",
"label_en": "Gold Tier",
"description_fr": "Bénéficiez de 15% de réduction sur tous vos achats Aksal.",
"description_ar": "استمتع بخصم 15% على جميع مشترياتك في أكسال.",
"description_en": "Enjoy 15% off on all your Aksal purchases.",
"progress": {
"percentage": 40,
"accumulated_mad": "80000.00",
"remaining_mad": "120000.00"
},
"remaining_for_next_tier": "120000.00",
"next_tier_labels": {
"label_fr": "Tier Platine",
"label_ar": "الدرجة البلاتينية",
"label_en": "Platinum Tier"
},
"is_employee": false
}
| Field | Type | Description |
|---|---|---|
tier_code | string | Stable, machine-readable tier identifier shared across environments, such as aksal_entry_tier, aksal_silver_tier, aksal_gold_tier, aksal_platinum_tier, aksal_diamond_tier, or aksal_employee_tier. |
first_name | string | Customer first name |
last_name | string | Customer last name |
customer_phone_number | string | Customer mobile number in international format |
balance | decimal | Customer points wallet balance |
discount_pct | decimal | Discount percentage for this tier. Can be null if not yet configured. |
label_fr | string | Tier name in French |
label_ar | string | Tier name in Arabic |
label_en | string | Tier name in English |
description_fr | string | Tier description in French. Can be null. |
description_ar | string | Tier description in Arabic. Can be null. |
description_en | string | Tier description in English. Can be null. |
progress | object | Backend-authoritative progress within the current tier's configured annual progression range. See below. |
remaining_for_next_tier | string | Legacy all-time-spend calculation retained for backward compatibility. New consumers should use progress.remaining_mad for annual tier progress. null when there is no next tier. |
next_tier_labels | object | Labels of the next tier (label_fr, label_ar, label_en). null when there is no next tier. |
is_employee | boolean | true when the customer holds the aksal_black_employees tag, false otherwise. |
progress uses approved sales from the Aksal brand configuration over the configured rolling annual window (currently 365 days). Amounts are clamped to the current tier range, so all three values describe the same progression interval.
| Field | Type | Description |
|---|---|---|
percentage | integer | Progress from 0 to 100, rounded to the nearest whole percentage. |
accumulated_mad | string | MAD accumulated after the current tier's lower threshold, capped at the next threshold. |
remaining_mad | string | MAD remaining before the next tier. |
Special cases:
- Entry customers use
aksal_entry_tierand progress from MAD 0 to the Silver threshold. - Diamond customers return
percentage: 100, withaccumulated_madandremaining_madset tonullbecause there is no next tier. - Employee customers return
nullfor all progress values because their tier is not spend-based. - Missing brand, window, stable-code, or threshold configuration returns
nullfor all progress values rather than reporting a misleading zero.
Status Code: 404
Returned when no customer is found for the given identifier, the customer is not enrolled in Aksal Black, or no tier has been assigned yet.
{
"detail": "No customer found."
}
{
"detail": "No tier assigned for this card."
}
2) Black card challenges
Retrieve active challenges tagged for the Aksal Black program, along with each customer's remaining run window.
GET /api/v1/partner/aksal/black-card/<identifier>/challenges
| Parameter | Type | Description |
|---|---|---|
identifier | string | The Aksal Black card number, the Aksal Black ID, or the customer's mobile phone number (see above) |
Sample request (card number)
GET https://app.kenzup.com/api/v1/partner/aksal/black-card/AKSAL-BLACK-0001/challenges
Sample request (mobile phone number)
GET https://app.kenzup.com/api/v1/partner/aksal/black-card/0611223344/challenges
Status Code: 200
[
{
"uuid": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"name_fr": "Défi Aksal Black",
"name_ar": "تحدي أكسال بلاك",
"name_en": "Aksal Black Challenge",
"description_fr": "Effectuez 3 achats en 48 heures.",
"description_ar": "قم بـ 3 مشتريات خلال 48 ساعة.",
"description_en": "Make 3 purchases within 48 hours.",
"remaining_run_window_hours": 36.5,
"bonus_amount": "50.00"
}
]
| Field | Type | Description |
|---|---|---|
uuid | uuid | Challenge identifier |
name_fr | string | Challenge name in French |
name_ar | string | Challenge name in Arabic |
name_en | string | Challenge name in English |
description_fr | string | Challenge description in French. Can be null. |
description_ar | string | Challenge description in Arabic. Can be null. |
description_en | string | Challenge description in English. Can be null. |
remaining_run_window_hours | decimal | Hours remaining in the customer's current run window. When the customer has no active in-progress run, this equals the challenge's configured run window duration. |
bonus_amount | decimal | Points bonus awarded when the challenge is completed. 0 if no bonus is configured. |
Only active challenges tagged with aksal_black (challenge tags) are returned. Returns an empty array when no matching challenges exist.
Status Code: 404
Returned when no customer is found for the given identifier or the customer is not enrolled in Aksal Black.
{
"detail": "No customer found."
}
3) Black card history
Retrieve purchase history and spending statistics for an Aksal Black customer. Only approved sales at brands linked to the customer's tier are included.
GET /api/v1/partner/aksal/black-card/<identifier>/history
| Parameter | Type | Description |
|---|---|---|
identifier | string | The Aksal Black card number, the Aksal Black ID, or the customer's mobile phone number (see above) |
Sample request (card number)
GET https://app.kenzup.com/api/v1/partner/aksal/black-card/AKSAL-BLACK-0001/history
Sample request (mobile phone number)
GET https://app.kenzup.com/api/v1/partner/aksal/black-card/0611223344/history
Status Code: 200
{
"total_sale_amount": "350.00",
"total_sale_amount_current_year": "150.00",
"total_sale_amount_current_month": "150.00",
"number_of_purchases_current_year": 2,
"number_of_purchases_current_month": 2,
"history": [
{
"created_at": "2025-06-15T12:00:00Z",
"brand_name": "Marjane",
"shop_system_name": "marjane-casablanca-01",
"earned_points": "10.00",
"burned_points": "0.00"
}
]
}
| Field | Type | Description |
|---|---|---|
total_sale_amount | decimal | Total approved sale amount across all time (tier brands only) |
total_sale_amount_current_year | decimal | Total approved sale amount for the current calendar year |
total_sale_amount_current_month | decimal | Total approved sale amount for the current calendar month |
number_of_purchases_current_year | integer | Number of approved purchases in the current calendar year |
number_of_purchases_current_month | integer | Number of approved purchases in the current calendar month |
history | array | List of individual approved sales (see below) |
Each item in history:
| Field | Type | Description |
|---|---|---|
created_at | datetime | Sale creation timestamp |
brand_name | string | Brand name of the shop |
shop_system_name | string | Shop system identifier |
earned_points | decimal | Points earned on this sale |
burned_points | decimal | Points burned on this sale |
Status Code: 404
Returned when no customer is found for the given identifier, the customer is not enrolled in Aksal Black, or no tier has been assigned yet.
{
"detail": "No customer found."
}
{
"detail": "No tier assigned for this card."
}
Common errors
Status Code: 403
Returned when the signature is invalid or the client_id is not authorized.
Outbound webhooks
Kenzup POSTs sale.created, tier.changed, and challenge.completed to AKSAL_WEBHOOK_URL for Aksal Black customers. Production uses https://api.aksal.ma/api/kenzup/webhook; staging uses https://api.staging.aksalblack.ma/api/kenzup/webhook. Nothing is sent until AKSAL_WEBHOOK_URL is set. points.expiring is not sent.
The JSON body is {customer, entity_id, event, occurred_at} with keys sorted alphabetically and no extra whitespace. customer is the Black card number, or the customer's phone number when no card is stored. entity_id is required for challenge.completed and optional elsewhere. tier.changed is sent for every tier move, including downgrades and first enrollment. Signing reuses the inbound X-SIGNATURE scheme with AKSAL_SERVICE_CLIENT_ID and RSA_PRIVATE_KEY. Any 2xx is an ack; non-2xx is retried for about 24 hours.
Export the public key with python manage.py export_rsa_public_key.