Grupy API
The Grupy API coordinates shared payment journeys between a group organizer and multiple participants, it provides the transaction coordination layer, participant state, authorization decisions, contribution tracking and reconciliation instructions.
Responsibility Boundary
Clear segregation of duties ensures PCI compliance and operational security across partner systems:
| Responsibility | Customer-Experience Partner | Payment or Banking Partner | Grupy |
|---|---|---|---|
| Render UI / Screens | Primary | Hosted payment UI only | Returns structured outcomes & next actions |
| Capture User Input | Primary | Payment authorization input only | Validates submitted business fields |
| Card & Bank Credentials | Never (unless PCI-authorized) | Primary in approved hosted environment | Never receives raw card or banking credentials |
| Transaction State | Channel-local state only | Provider-local payment state | Authoritative Grupy transaction state |
| Group Coordination | Presents journey | Reports payment outcome | Applies group rules & contribution transitions |
| Payment Processing | Redirects / invokes approved flow | Primary | Requests & reconciles; no raw card processing |
| Idempotency | Reuses identifiers during retries | Reuses provider event references | Prevents duplicate state transitions |
| Audit References | Preserves event & correlation IDs | Preserves payment references | Maintains Grupy-side audit log outcome |
API Fundamentals
Current Base URL
https://api.grupy.co.zaThis works for both test and live environments. Your API keys determine which environment you're using.
Endpoints
| Method | Path | Purpose |
|---|---|---|
| GET | /health | Basic service liveness check of the Grupy API |
| GET | /ready | High-level integration readiness check |
| GET | /internal/ready | Internal readiness probe; requires Authorization: Bearer <INTERNAL_API_BEARER_TOKEN> |
| POST | /partner/clickatell/events | Clickatell workflow and Chat2Pay pilot events |
Request Conventions
- Content Type:
application/json(UTF-8) - Maximum Body Size: 1 MB limit per request
- Dates & Times: ISO 8601 in UTC, for example
2026-08-02T14:30:00Z - Phone Numbers: E.164 digits, for example
27821234567 - Currency: Uppercase ISO 4217 code, Phase 1 is
ZAR - Monetary Amounts: Integer minor units only ending in
Minor(e.g.2400000for ZAR 24,000.00) - Identifiers: Opaque strings; partners must not infer business meaning from format
Authentication & Request Integrity
The live partner endpoint (/partner/clickatell/events) requires HMAC-SHA256 request signing using CLICKATELL_PARTNER_SECRET. The secret is exchanged via secure channel and never exposed in logs or documentation.
Required HTTP Headers
Content-Type: application/json
X-Grupy-Timestamp: 1785386400
X-Grupy-Signature: sha256=<lowercase-hex-hmac>
Idempotency-Key: clickatell-event-evt_01JXYZSignature Calculation Input: <timestamp>.<exact-raw-request-body>
Signing Formula: hex(HMAC-SHA256(partner_secret, signature_input))
const crypto = require('crypto');
const timestamp = Math.floor(Date.now() / 1000).toString();
const rawBody = JSON.stringify(payload);
const signature = crypto
.createHmac('sha256', partnerSecret)
.update(`${timestamp}.${rawBody}`)
.digest('hex');
// Send HTTP Request with headers:
// X-Grupy-Timestamp: timestamp
// X-Grupy-Signature: sha256=${signature}Credential Rules
- Use a different credential per partner and environment.
- Store credentials strictly in a server-side secret manager.
- Never place credentials in browser, mobile app, URL, analytics, or source repo.
- Rotate credentials immediately if exposure is suspected.
- Contact Grupy before rotating active production credentials to coordinate cutover.
Idempotency & Duplicate Prevention
Every state-changing event requires a globally unique and stable event identifier passed via the Idempotency-Key header.
- Use 8 to 128 characters (letters, digits,
.,_,:,-). - Reuse the exact same key when retrying the exact same request.
- Never reuse a key for a corrected or different payload.
- An exact replay returns the original response and includes
X-Idempotent-Replay: true. - Reusing a key with different content returns
HTTP 409 idempotency_payload_mismatch. - A request still processing returns
HTTP 409 idempotency_request_in_progress.
Money & Precision
Grupy represents all monetary values strictly as integer minor units. Floating-point numbers and decimal money strings are prohibited on the API boundary.
| Display Value | Currency | API Value (integer minor units) |
|---|---|---|
| R0.01 | ZAR | 1 |
| R100.00 | ZAR | 10000 |
| R1,234.56 | ZAR | 123456 |
{
"amountMinor": 123456,
"currency": "ZAR"
}Event & Action Model
The Grupy Partner API is event-driven. Events report actions in partner channels; Grupy responses return instructions for the next action.
{
"eventType": "flow_completed",
"eventId": "evt_demo_01JXYZ",
"phoneNumber": "27821234567",
"contactId": "partner_contact_123",
"sessionId": "partner_session_123",
"flowName": "grupy_onboarding",
"flowData": {}
}{
"received": true,
"duplicate": false,
"channel": "whatsapp",
"recipient": "27821234567",
"contactId": "partner_contact_123",
"sessionId": "partner_session_123",
"messages": [
{
"type": "text",
"text": "Your request was accepted."
}
],
"nextAction": "show_main_menu"
}Core Event Families
| Event Family | Purpose |
|---|---|
| Identity & Onboarding | Establish approved Grupy profile (display name, email, PIN & legal consent) |
| User Authorization | Confirm user authorization for protected actions |
| Group Creation | Define purpose, total amount, participants, due date, split mode |
| Participant Action | Join, approve, decline, or view group status |
| Payment Method Readiness | Report provider-issued token or reference without raw card data |
| Payment Lifecycle | Report pending, success, failure, expiry, cancellation, reversal |
| Payout & Settlement | Report payout, settlement, refund, dispute outcomes |
| Operational Exception | Suspend, recover, review, or reconcile exceptional transactions |
Response & Messaging Delivery Model
The primary Phase 1 delivery model is synchronous:
- Clickatell sends one signed event to
POST /partner/clickatell/events. - Grupy processes the event and returns
messages,nextAction, routing information, and Flow/template parameters in the HTTP response. - Clickatell renders and delivers the corresponding WhatsApp screen, message, invitation, or Chat2Pay action.
- If delivery fails, Clickatell retries the original event with the same
eventIdandIdempotency-Key; Grupy returns the cached response without duplicating transactions.
Asynchronous Outbound Alternative: If Clickatell requires Grupy to invoke an outbound send-message API rather than receiving synchronous response instructions, Clickatell must supply the outbound endpoint, OAuth/bearer scheme, message schemas, and delivery webhooks.
WhatsApp Delivery Policy
- Session Messages: Responses to active inbound user interactions use session messages while the 24-hour customer-service window is open.
- Template Messages: Out-of-window messages (e.g. invitations, payment reminders) use approved WhatsApp templates such as
grupy_group_invitation_v1. - Window Management: Clickatell owns 24-hour window tracking and template rendering. Grupy returns stable template keys and parameter maps.
Clickatell Native Payload Mapping
Clickatell may send canonical Grupy field names directly or use the accepted aliases below. Ambiguous or unknown fields produce validation errors rather than guessed updates.
| Meaning | Canonical Grupy Field | Accepted Aliases | Clickatell Confirmation Required |
|---|---|---|---|
| Stable Event ID | eventId | event_id, messageId, message_id, message.id | Native unique ID & retention period |
| Event Category | eventType | event_type, type | Native Flow & Chat2Pay event names |
| WhatsApp Number | phoneNumber | phone_number, msisdn, whatsappNumber, from | Format & MSISDN verification |
| Contact Identifier | contactId | contact_id, contact.id | Native Clickatell contact ID |
| Session Identifier | sessionId | session_id | Session ID & 24h window metadata |
| Flow Name / Key | flowName | flowKey, flow_key, flow_name, flow.key | Published Flow IDs / keys |
| Submitted Flow Values | flowData | flow_data, fields, flow.data, payload.fields | Native submission object & field IDs |
| Group Reference | groupReference | group_reference, referenceCode, reference_code | Carried reference in Chat2Pay callbacks |
| Attempt Reference | attemptReference | attempt_reference, linkReference, link_reference | Stable link & lifecycle identifier |
| Provider Payment Ref | paymentReference | payment_reference | Native successful payment reference |
| Monetary Amount | amountMinor | amount_minor | Cents vs. Rand unit conversion |
| Event Time | occurredAt | occurred_at, timestamp | ISO-8601 UTC timestamp format |
| Link Expiry Time | expiresAt | expires_at | Chat2Pay link expiry behavior |
Current Pilot Event Payloads
{
"eventType": "flow_completed",
"eventId": "onboarding_001",
"phoneNumber": "27821234567",
"contactId": "ct_contact_123",
"flowName": "grupy_onboarding",
"flowData": {
"displayName": "Lebo Mokoena",
"email": "lebo@example.com",
"pin": "482604",
"pinConfirmation": "482604",
"consent": {
"termsAccepted": true,
"privacyAccepted": true,
"marketingOptIn": false
}
}
}WhatsApp Flow name: grupy_pin (establishes 5-minute server authorization window):
{
"eventType": "flow_completed",
"eventId": "pin_001",
"phoneNumber": "27821234567",
"flowName": "grupy_pin",
"flowData": {
"pin": "482604",
"requestedAction": "create_group"
}
}Success response:
{
"verified": true,
"authorizationExpiresInSeconds": 300,
"nextAction": "create_group"
}Card details captured strictly on Clickatell's hosted PCI checkout page. No PAN or CVV returned:
{
"eventType": "payment_method_ready",
"eventId": "payment_method_001",
"phoneNumber": "27821234567",
"provider": "chat2pay",
"customerReference": "cust_123",
"paymentMethodReady": true
}WhatsApp Flow name: grupy_create_payment (equal split up to 50 members, ZAR only):
{
"eventType": "flow_completed",
"eventId": "create_001",
"phoneNumber": "27821234567",
"flowName": "grupy_create_payment",
"flowData": {
"title": "Weekend accommodation",
"totalAmountMinor": 2400000,
"dueOn": "2026-08-15",
"splitMode": "equal",
"includeOrganizer": true,
"members": [
"27825550101",
"27825550102"
],
"settlementDestination": {
"type": "organizer",
"label": "Lebo Mokoena"
}
}
}settlementDestination.type supports: organizer (organizer MSISDN), participant (requires recipientPhoneNumber), or merchant (requires non-sensitive merchantReference).
Illustrative success response:
{
"received": true,
"nextAction": "send_invitations",
"group": {
"referenceCode": "GRP-1234ABCD",
"status": "pending_members",
"includeOrganizer": true
},
"invitations": [
{
"to": "27825550101",
"action": {
"type": "open_flow",
"flowKey": "join_group",
"parameters": {
"groupReference": "GRP-1234ABCD"
}
}
}
]
}Fallback command/action event when WhatsApp Flows are not rendered:
{
"eventType": "message",
"eventId": "message_001",
"phoneNumber": "27821234567",
"action": "join GRP-1234ABCD"
}{
"eventType": "chat2pay_payment_succeeded",
"eventId": "payment_001",
"phoneNumber": "27825550101",
"groupReference": "GRP-1234ABCD",
"paymentReference": "c2p_payment_123",
"amountMinor": 80000,
"currency": "ZAR",
"status": "succeeded",
"provider": "chat2pay"
}Payment Lifecycle & Events
Supported Chat2Pay lifecycle events:
| External State / Event | Meaning for Partner |
|---|---|
payment_link_created | Chat2Pay payment link generated for participant. |
payment_link_clicked | Participant clicked payment link to open checkout. |
payment_pending | Customer authorization / 3DS in progress. |
chat2pay_payment_succeeded | Authoritative payment success; contribution marked paid. |
payment_failed | Payment attempt failed; participant may retry. |
payment_expired | Payment link expired without successful payment. |
payment_cancelled | Payment attempt cancelled by user or provider. |
payment_reversed | Payment outcome reversed; flags support review without silent balance changes. |
Every payment attempt event includes attemptReference, groupReference, phoneNumber, amountMinor, currency, and ISO-8601 occurredAt:
{
"eventType": "payment_pending",
"eventId": "payment_event_001",
"phoneNumber": "27825550101",
"groupReference": "GRP-1234ABCD",
"attemptReference": "c2p_attempt_123",
"paymentReference": null,
"amountMinor": 80000,
"currency": "ZAR",
"provider": "chat2pay",
"occurredAt": "2026-08-06T10:15:30Z",
"expiresAt": "2026-08-06T10:30:30Z"
}Errors, Retries & HTTP Contract
| HTTP Code | Meaning | Partner Action |
|---|---|---|
| 200 | Event accepted / replay acknowledged | Read response body & follow nextAction. |
| 201 | Group or resource created | Store returned opaque reference code. |
| 400 | Invalid JSON or missing required field | Correct request payload before retrying. |
| 401 | Authentication / HMAC signature failed | Verify HMAC secret, signature calculation & clock sync. |
| 403 | Forbidden / action not allowed for user | Verify user PIN authorization or group state. |
| 404 | Referenced Grupy object does not exist | Verify reference code & resource ID. |
| 409 | Idempotency payload mismatch / request in progress | Follow returned error & Retry-After guidance. |
| 413 | Request exceeds 1 MB limit | Reduce payload size; do not send binary data. |
| 422 | Flow validation / prohibited credential / reconciliation failed | Show safe guidance; check for prohibited card fields. |
| 429 | Rate limit exceeded | Wait for Retry-After, then retry with same identifiers. |
| 500 | Unexpected server error | Retry with exponential backoff & original eventId. |
| 503 | Schema or dependency temporarily unavailable | Retry with exponential backoff; escalate if sustained. |
Standard Error Response Shape
{
"error": "machine_readable_code",
"message": "Safe partner-facing explanation",
"correlationId": "corr_demo_123"
}Retry & Timeout Guidance
- Every event must contain a unique, stable
eventId. - Retries must reuse the original
eventIdandIdempotency-Key. - Clickatell may retry transient
5xxresponses with exponential backoff. - Do not retry corrected
4xxclient errors under the sameeventId. - Respect
Retry-Afterheaders on429responses.
Security & Data Handling
Security Commitments
- All partner traffic requires HTTPS (TLS 1.2+).
- State-changing events require HMAC-SHA256 authenticated server requests.
- Event replay is protected using signed timestamps and idempotency keys.
- Sensitive values are redacted from normal service logs.
- Raw cardholder data is never accepted on the Grupy partner API.
Prohibited Data
Clickatell & partners must NEVER send any of the following fields to Grupy:
- Full credit card number or PAN
- CVV / CVC security codes
- Card, banking, or device OTPs
- Banking PIN or card PIN
- Masked card numbers, expiry dates, or card fingerprints
- Raw card/token references on payment readiness events
Grupy PIN Security
The Grupy authorization PIN is a 6-digit application credential (not a card/bank PIN). Validating a PIN establishes a 5-minute (300s) server-side authorization window (authorizationExpiresInSeconds: 300). 5 failed PIN attempts lock verification for 15 minutes.
Clickatell Decisions Required
Key technical items and design decisions required from Clickatell prior to production go-live:
- Confirm production callback authentication and whether HMAC signing (
X-Grupy-Signature) is supported. - Provide actual Chat Flow request and response schemas.
- Provide Chat2Pay link-generation endpoint, authentication, expiry fields, callback event IDs, timestamp semantics, and exact minor-unit/decimal conversion requirements.
- Confirm 3DS/SCA browser behavior and supply sandbox cards for success, failure, timeout, and challenge tests.
- Confirm WhatsApp 24-hour window indicators, approved template names, and failed-delivery retry responsibilities.
- Confirm whether Grupy returns a synchronous screen instruction or calls a separate Clickatell send-message API.
- Provide Chat2Pay payment-method-ready, payment-created, succeeded, failed, expired, reversed, refunded, disputed, and payout callbacks.
- Confirm that Clickatell retains token ownership; also confirm the merchant account, settlement account, fees, reconciliation reports, and refund authority.
- Confirm whether saved-card repeat payments require a user button, text approval, 3DS, or another authorization.
- Confirm webhook retry schedule, maximum retention period, source IP ranges, and request timeout.
- Confirm the official WhatsApp business number, display name, templates, and support escalation path.
