Last updated August 2026

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:

ResponsibilityCustomer-Experience PartnerPayment or Banking PartnerGrupy
Render UI / ScreensPrimaryHosted payment UI onlyReturns structured outcomes & next actions
Capture User InputPrimaryPayment authorization input onlyValidates submitted business fields
Card & Bank CredentialsNever (unless PCI-authorized)Primary in approved hosted environmentNever receives raw card or banking credentials
Transaction StateChannel-local state onlyProvider-local payment stateAuthoritative Grupy transaction state
Group CoordinationPresents journeyReports payment outcomeApplies group rules & contribution transitions
Payment ProcessingRedirects / invokes approved flowPrimaryRequests & reconciles; no raw card processing
IdempotencyReuses identifiers during retriesReuses provider event referencesPrevents duplicate state transitions
Audit ReferencesPreserves event & correlation IDsPreserves payment referencesMaintains Grupy-side audit log outcome

API Fundamentals

Current Base URL

https://api.grupy.co.za

This works for both test and live environments. Your API keys determine which environment you're using.

Endpoints

MethodPathPurpose
GET/healthBasic service liveness check of the Grupy API
GET/readyHigh-level integration readiness check
GET/internal/readyInternal readiness probe; requires Authorization: Bearer <INTERNAL_API_BEARER_TOKEN>
POST/partner/clickatell/eventsClickatell workflow and Chat2Pay pilot events

Request Conventions

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.

HMAC Headers

Required HTTP Headers

Content-Type: application/json
X-Grupy-Timestamp: 1785386400
X-Grupy-Signature: sha256=<lowercase-hex-hmac>
Idempotency-Key: clickatell-event-evt_01JXYZ

Signature Calculation Input: <timestamp>.<exact-raw-request-body>
Signing Formula: hex(HMAC-SHA256(partner_secret, signature_input))

Signing Example
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}
WarningRequests with a missing or invalid signature are rejected. Timestamp must be within 300 seconds (5 minutes) of Grupy's server clock.

Credential Rules

Idempotency & Duplicate Prevention

Every state-changing event requires a globally unique and stable event identifier passed via the Idempotency-Key header.

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 ValueCurrencyAPI Value (integer minor units)
R0.01ZAR1
R100.00ZAR10000
R1,234.56ZAR123456
Minor Units Example
{
  "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.

Common Request Envelope
{
  "eventType": "flow_completed",
  "eventId": "evt_demo_01JXYZ",
  "phoneNumber": "27821234567",
  "contactId": "partner_contact_123",
  "sessionId": "partner_session_123",
  "flowName": "grupy_onboarding",
  "flowData": {}
}
Common Response Envelope
{
  "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 FamilyPurpose
Identity & OnboardingEstablish approved Grupy profile (display name, email, PIN & legal consent)
User AuthorizationConfirm user authorization for protected actions
Group CreationDefine purpose, total amount, participants, due date, split mode
Participant ActionJoin, approve, decline, or view group status
Payment Method ReadinessReport provider-issued token or reference without raw card data
Payment LifecycleReport pending, success, failure, expiry, cancellation, reversal
Payout & SettlementReport payout, settlement, refund, dispute outcomes
Operational ExceptionSuspend, recover, review, or reconcile exceptional transactions

Response & Messaging Delivery Model

The primary Phase 1 delivery model is synchronous:

  1. Clickatell sends one signed event to POST /partner/clickatell/events.
  2. Grupy processes the event and returns messages, nextAction, routing information, and Flow/template parameters in the HTTP response.
  3. Clickatell renders and delivers the corresponding WhatsApp screen, message, invitation, or Chat2Pay action.
  4. If delivery fails, Clickatell retries the original event with the same eventId and Idempotency-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

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.

MeaningCanonical Grupy FieldAccepted AliasesClickatell Confirmation Required
Stable Event IDeventIdevent_id, messageId, message_id, message.idNative unique ID & retention period
Event CategoryeventTypeevent_type, typeNative Flow & Chat2Pay event names
WhatsApp NumberphoneNumberphone_number, msisdn, whatsappNumber, fromFormat & MSISDN verification
Contact IdentifiercontactIdcontact_id, contact.idNative Clickatell contact ID
Session IdentifiersessionIdsession_idSession ID & 24h window metadata
Flow Name / KeyflowNameflowKey, flow_key, flow_name, flow.keyPublished Flow IDs / keys
Submitted Flow ValuesflowDataflow_data, fields, flow.data, payload.fieldsNative submission object & field IDs
Group ReferencegroupReferencegroup_reference, referenceCode, reference_codeCarried reference in Chat2Pay callbacks
Attempt ReferenceattemptReferenceattempt_reference, linkReference, link_referenceStable link & lifecycle identifier
Provider Payment RefpaymentReferencepayment_referenceNative successful payment reference
Monetary AmountamountMinoramount_minorCents vs. Rand unit conversion
Event TimeoccurredAtoccurred_at, timestampISO-8601 UTC timestamp format
Link Expiry TimeexpiresAtexpires_atChat2Pay link expiry behavior

Current Pilot Event Payloads

1. Onboarding Submitted
{
  "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
    }
  }
}
2. PIN Challenge Submitted

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"
}
3. Chat2Pay Payment Method Ready

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
}
4. Create Equal-Split Group

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"
        }
      }
    }
  ]
}
5. Menu or Message Action

Fallback command/action event when WhatsApp Flows are not rendered:

{
  "eventType": "message",
  "eventId": "message_001",
  "phoneNumber": "27821234567",
  "action": "join GRP-1234ABCD"
}
6. Chat2Pay Payment Succeeded
{
  "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 / EventMeaning for Partner
payment_link_createdChat2Pay payment link generated for participant.
payment_link_clickedParticipant clicked payment link to open checkout.
payment_pendingCustomer authorization / 3DS in progress.
chat2pay_payment_succeededAuthoritative payment success; contribution marked paid.
payment_failedPayment attempt failed; participant may retry.
payment_expiredPayment link expired without successful payment.
payment_cancelledPayment attempt cancelled by user or provider.
payment_reversedPayment outcome reversed; flags support review without silent balance changes.
Representative Lifecycle Event Callback

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 CodeMeaningPartner Action
200Event accepted / replay acknowledgedRead response body & follow nextAction.
201Group or resource createdStore returned opaque reference code.
400Invalid JSON or missing required fieldCorrect request payload before retrying.
401Authentication / HMAC signature failedVerify HMAC secret, signature calculation & clock sync.
403Forbidden / action not allowed for userVerify user PIN authorization or group state.
404Referenced Grupy object does not existVerify reference code & resource ID.
409Idempotency payload mismatch / request in progressFollow returned error & Retry-After guidance.
413Request exceeds 1 MB limitReduce payload size; do not send binary data.
422Flow validation / prohibited credential / reconciliation failedShow safe guidance; check for prohibited card fields.
429Rate limit exceededWait for Retry-After, then retry with same identifiers.
500Unexpected server errorRetry with exponential backoff & original eventId.
503Schema or dependency temporarily unavailableRetry 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

Security & Data Handling

Security Commitments

Prohibited Data

Clickatell & partners must NEVER send any of the following fields to Grupy:

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: