Authentication
  • Introduction
  • Authentication
  • Responses
  • Refecence Data
  • Localisation
  • Querying
  • Paging
  • Batching
  • Method Usage Limit
  • Terminology
  • Error Codes
 
Eenvoudig en veilig. Geen terugkerende maandelijkse kosten

Geef uw bedrijf kracht met onze flexibele API

We werken samen met een groeiend netwerk van 8000 partners wereldwijd, waaronder OTT-platforms, langeafstandsbelapps, financiële instellingen, retailers, POS-providers en meer.

TABLE OF CONTENTS

  • Introduction
  • Authentication
  • Test with Postman
  • Deferred SendTransfer
  • Responses
  • Reference Data
  • Localization
  • Querying
  • Paging
  • Batching
  • Method Usage Limit
  • Terminology
  • Error Codes
  • Optional Features

Introduction

The Ding API is a Level 0 REST web service. We have used the swagger standard to describe this service. As a result, we are able to provide an interactive documentation page, which lists all the methods and even allows you to interact with API, showing the request bodies and responses that you will receive from the API.

To generate an API Key, you should go to the Developer tab which can be found in the Account Settings section of your DingConnect account.

Enter the API Key into the token input box on top-right of the Methods page Methods and click the button. Then browse the API definition and interact with the API from that page. Using this interface you can figure out the headers, request bodies and responses that your software will need to send to interact with the API.

The swagger definition can also be used to automatically generate client SDK libraries in many different languages. Many free and commercial code generators are available that can consume this swagger definition and generate client code for you, allowing you to rapidly develop your application. For example, you can import our swagger definition into the online service provided at http://editor.swagger.io and from that interface you can generate client code in over 20 different programming languages.

The URL for our swagger definition is in the URL text box on the Methods page and ends with /swagger/docs/v1.

Authentication

The DingConnect API supports authorisation using API Keys and OAuth. For any new integration, our recommendation is to use OAuth.

All requests to the API must be authenticated. Any exceptions to this rule are documented in the individual API methods.

All requests can include an optional header X-Correlation-Id that can be used to correlate HTTP requests between a client and server. This header will be useful to diagnose potential issues that integrated clients might encounter.

API Keys

The client must include an HTTP header api_key using the API key that was issued to you.

If authentication fails, you will receive a HTTP 401 response.

OAuth

The only supported OAuth flow is the client credentials flow, which is used only for server-to-server API requests. In order to use OAuth, a new OAuth client must be registered in your DingConnect account.

OAuth credentials are managed from within the DingConnect portal, by navigating to the Developer tab in Account Settings.

Once new OAuth credentials are generated, a bearer token can be requested from https://idp.ding.com/connect/token with the client_id and client_secret. The returned token must be included in the Authorization header with the “Bearer” authentication scheme. A new token must be requested once the current token expires.

Supported identity provider configuration can be obtained from: https://idp.ding.com/.well-known/openid-configuration, and used programmatically with client libraries.

Example response


{
    "issuer": "https://idp.ding.com",
    "jwks_uri": "https://idp.ding.com/.well-known/openid-configuration/jwks",
    "token_endpoint": "https://idp.ding.com/connect/token",
    "token_endpoint_auth_methods_supported": [
        "client_secret_basic",
        "client_secret_post"
    ],
    "id_token_signing_alg_values_supported": [
        "RS256"
    ]
}
                    

Getting Tokens

In order to request a token, a valid client_id and client_secret must be used, as documented above.

Parameter Type Description
client_id string Client Id
client_secret string Client secret
grant_type string Must be client_credentials.

Token will stay valid for the duration that was specified when creating the client/credentials.

Example request


POST https://idp.ding.com/connect/token HTTP/1.1
Host: idp.ding.com
Content-Type: application/x-www-form-urlencoded
client_id=44c61ca4-b3c0-4604-9d25-ad1e486f33f2
&client_secret=4mVCwicZXgbB6wJFPeh1D+xLlkeW34JN+y449hZYdUc=
&grant_type=client_credentials
                    

Example response


{
    "access_token": "eyJhbGciOiJSUzI1NiIsImtpZCI6IjFDQTY1OUI2ODk
                    0RTdDQzNDRDc5MjM1OTNBMEFCNTY1IiwidHlwIjoiYXQ
                    rand0In0.eyJuYmYiOjE1OTg1MjY3MDQsImV4cCI6MTU
                    5ODUyODUwNCwiaXNzIjoiaHR0cDovL2lkcHNlcnZpY2U
                    6OTkwMCIsImF1ZCI6ImRpbmdjb25uZWN0YXBpIiwiY2x
                    pZW50X2lkIjoiNDRjNjFjYTQtYjNjMC00NjA0LTlkMjU
                    tYWQxZTQ4NmYzM2YyIiwianRpIjoiQUYxQUFENTU1RUM
                    zQzk3NUIyNkVCQjFDRjhGM0Q4NTQiLCJpYXQiOjE1OTg
                    1MjY3MDQsInNjb3BlIjpbInRvcHVwYXBpIl19.nEFikzy
                    qNk_YjFOyJ95n_HLebhP7jW4FzPVPEKdLpBZaC8-7CELj
                    NDcbSnfxBKMBq1xb506azZ5dcs6AVVHHKBsDfKww8pQyL
                    -R1SBuGN1BHtYpr-DKYnR52Y2Ga7BR5CQ0owtTmLk24z_
                    Fze2Ocw0eZpFk_3tLldrYg92w4shq88tUHy_9xLXnfch
                    sTND6Zpx4nav8cSR1vFmdEIIkcaMJzpVdiuvZcSrSbkd
                    2HVIKqq4K8jfP572EdeAk0ZW7jWsv2BJlk0HvpFW6s6q
                    TTXHToF7ZJgF0GSRSBiWn_E1Sr1bZxxHB55rR3v8y8r_
                    7TVkfmWRvZTDzHfqxCih7j5g",
    "expires_in": 1800,
    "token_type": "Bearer",
    "scope": "topupapi"
}
                    

Please note that expire_in value is in seconds.

Using Tokens

The Authorization header must be populated with the access token using the “Bearer” authentication scheme.

Example request


GET https://api.dingconnect.com/api/V1/GetRegions HTTP/1.1
Host: api.dingconnect.com
Authorization: Bearer eyJhbGciOiJSUzI1NiIsImtpZCI6IjFDQTY1OU
                      I2ODk0RTdDQzNDRDc5MjM1OTNBMEFCNTY1Iiwi
                      dHlwIjoiYXQrand0In0.eyJuYmYiOjE1OTgzNz
                      MwOTEsImV4cCI6MTU5ODM3NDg5MSwiaXNzIjoi
                      aHR0cDovL2lkcHNlcnZpY2U6OTkwMCIsImF1ZC
                      I6ImRpbmdjb25uZWN0YXBpIiwiY2xpZW50X2lk
                      IjoiN2Q5ZmVlOTAtZjFjYi00NmNhLTlkMTUtYz
                      YyNmQxMjkxYjFmIiwianRpIjoiRjY3RjNDMDFF
                      MURGMDY4REFCNTIxMjVGMjM4NTREOEYiLCJpYX
                      QiOjE1OTgzNzMwOTEsInNjb3BlIjpbInRvcHVw
                      YXBpIl19.aglCy-aPOIqNR5W9lX9dBrd0mQSGb
                      mv_3kHAAtZpbq_67fCefLe2us-TjpZ_RQJ1cud
                      365rfB4TPXzoVpGGPY4E2dzu1jQ8Ubz2PsroO9
                      fgizcxgaic_Lwl5apHddvWdl9evgB90z1kXPXl
                      PZBEoxdFqkpOFt4HaKk2iOcfsoBDudvvdoT_xQ
                      SklJkAmInfLN9SKms5TgeIifwIEOwvmKcBMMaJ
                      UoaPyMv1af8FysyQanZFuem-Yt5deSPOenryZS
                      sCrubih59aF36nPU7J6lmQWdtmSBSMbrEfXlzG
                      JRanlpHQPTplNTynVSAegKZa9L-NpeI3GbQ5qa
                      gfKLE0SOg
                

When a token expires, a new token must be requested, as documented above in “Getting Tokens”

Token expired example response


{ 
    "ResultCode": 4, 
    "ErrorCodes": [ 
        { 
            "Code": "AuthenticationFailed", 
            "Context": "TokenExpired" 
        }
    ]
}
                    

Test with Postman

Test the API by clicking the Run In Postman button. This will create a fork of our API Collection in Postman. Once loaded, edit the Collection variables to add your API key to the api_key variable, and you’re all set.

Deferred SendTransfer

Deferred SendTransfer is a special type of SendTransfer that gives you the ability to get notified with HTTP callback (aka webhook) when the transfer is completed. You will get an immediate response with ProcessingState=Submitted and the final transfer result will be sent as webhook notification.

When there are failures the transfer will be retried until it is completed successfully or the maximum retry threshold is reached and only then will a notification be sent.

To send deferred transfer, you need to:

  1. Expose a response notification endpoint
  2. Setup webhook on DingConnect
  3. Provide additional headers with SendTransfer request

Expose a response notification endpoint

SendTransfer response notifications are sent using HTTP callbacks (aka webhook). Your endpoint needs to be public. DingConnect only supports HTTPS endpoints.
Consider adding the Ding domain and IP addresses to your firewall whitelist. For more information please contact support.

Setup webhook on DingConnect

  1. Login to your DingConnect account
  2. Go to Settings > Developer > Webhooks
  3. Enter your server endpoint
  4. Save the configuration

Provide Additional headers with the SendTransfer request

To send deferred SendTransfer, you need to:

  1. Create a standard SendTransfer request
  2. Specify X-Options and X-Correlation-Id headers. Where X-Options is set to DeferTransfer and X-Correlation-Id is set to the id number that you will use to identify this send transfer when you receive response notification.

When the SendTransfer request is sent using X-Option: DeferTransfer header response will be returned immediately with the ProcessingState set to “Submitted” and final transaction result will be sent to the notification server endpoint previously setup. The notification response will contain X-Correlation-Id header you provided.

Example request


POST https://api.dingconnect.com/api/V1/SendTransfer HTTP/1.1
Host: idp.ding.com
Content-Type: application/json
X-Options: DeferTransfer
X-Correlation-Id: 95E0578C-247C-4235-8AAC-BB901BBCCA9B

{
    "SkuCode": "AF_AW_TopUp",
    "SendValue" : 1,
    "AccountNumber" : "93000000000",
    "DistributorRef" : "12345",
    "ValidateOnly" : false
}

Example response


{
    "TransferRecord": {
    "TransferId": {
    "TransferRef": "134",
    "DistributorRef": "12345"
    },
    "SkuCode": "AF_AW_TopUp",
    "Price": {
    "CustomerFee": 0.00,
    "DistributorFee": 0.00,
    "ReceiveValue": 76.0,
    "ReceiveCurrencyIso": "AFN",
    "ReceiveValueExcludingTax": 68.40,
    "TaxRate": 10.0000,
    "TaxName": "AIT",
    "TaxCalculation": "Inclusive",
    "SendValue": 1.0,
    "SendCurrencyIso": "USD"
    },
    "CommissionApplied": 0.0,
    "StartedUtc": "2021-12-06T14:32:50Z",
    "CompletedUtc": "2021-12-06T14:32:51Z",
    "ProcessingState": "Submitted",
    "ReceiptText": null,
    "ReceiptParams": null,
    "AccountNumber": "93000000000"
    },
    "ResultCode": 1,
    "ErrorCodes": []
}

Example of webhook response

When request is competed endpoint will be called with completed transfer response.


{
    "TransferRecord": {
    "TransferId": {
    "TransferRef": "33",
    "DistributorRef": "12345"
    },
    "SkuCode": "AF_AW_TopUp",
    "Price": {
    "CustomerFee": 0,
    "DistributorFee": 0,
    "ReceiveValue": 76.0,
    "ReceiveCurrencyIso": "AFN",
    "ReceiveValueExcludingTax": 68.40,
    "TaxRate": 10.000,
    "TaxName": null,
    "TaxCalculation": null,
    "SendValue": 5,
    "SendCurrencyIso": "USD"
    },
    "CommissionApplied": 0,
    "StartedUtc": "2021-12-09T21:40:15Z",
    "CompletedUtc": "2021-12-09T21:40:15Z",
    "ProcessingState": "Complete",
    "ReceiptText": null,
    "ReceiptParams": null,
    "AccountNumber": "93000000000"
    },
    "ResultCode": 1,
    "ErrorCodes": []
}

Webhook Signature Verification

Ding webhooks include cryptographic signatures in every request, allowing you to verify that the events were sent by Ding and haven't been tampered with during transmission. This verification is strongly recommended for production environments.

Overview

Ding uses asymmetric cryptography (RSA with PKCS#1 v1.5 padding and SHA-256, as indicated by the RS256 algorithm) to sign webhook payloads. This means:

  • Ding signs requests using a private key
  • You verify signatures using the corresponding public key
  • Only Ding can create valid signatures, but anyone with the public key can verify them

Signature Headers

Every Ding webhook includes these headers:

Header Description Example
X-Ding-Webhook-Signature Contains timestamp and signature in format t={timestamp},v1={signature} t=1756234923,v1=SDbnyU+sly/7eE...
X-Ding-Webhook-Timestamp Unix timestamp when webhook was sent 1756234923
X-Ding-Webhook-Algorithm Signature algorithm used rs256
X-Ding-Webhook-Key-Id Identifier for the public key to use for verification eLE7vpn8EjfKzOzG-q8JgzqW-ew

Step-by-Step Verification Process

Step 1: Retrieve the Public Key

Fetch the current public keys from our JWKS endpoint:

GET https://idp.ding.com/.well-known/webhook-keys

This returns a JSON Web Key Set (JWKS) containing the public keys:

{
  "keys": [
    {
      "alg": "RS256",
      "e": "AQAB", 
      "kid": "eLE7vpn8EjfKzOzG-q8JgzqW-ew",
      "kty": "RSA",
      "n": "mvpLXqU8RCHue+nrdMuvF...",
      "use": "sig"
    }
  ]
}

Find the key where the kid field matches your X-Ding-Webhook-Key-Id header value.

Important: Cache this response but refresh it periodically as we rotate keys.

Step 2: Verify the Algorithm

Check that the X-Ding-Webhook-Algorithm header matches the expected algorithm:

  • Verify it equals rs256
  • Confirm it matches the alg field in the corresponding JWK
  • Reject webhooks with unexpected algorithms for security

Step 3: Parse the Signature Header

Extract the timestamp and signature from the X-Ding-Webhook-Signature header:

  • Split on comma: ["t=1756234923", "v1=SDbnyU+sly/7eE..."]
  • Extract timestamp: 1756234923
  • Extract signature: SDbnyU+sly/7eE... (base64 encoded)

Step 4: Verify the Timestamp

Compare the extracted timestamp against your current system time:

  • Convert timestamp to your system's current time format
  • Verify the webhook was sent recently (recommend: within 5 minutes)
  • Reject webhooks that are too old to prevent replay attacks

Step 5: Prepare the Signed Payload

Create the exact string that was signed by concatenating:

raw_request_body.timestamp.

Critical: Use the raw request body exactly as received. Do not:

  • Parse it as JSON first
  • Add or remove whitespace
  • Change encoding
  • Reorder fields

Example:

{"TransferRecord":{"TransferId":{"TransferRef":"1460","DistributorRef":"123433"},"SkuCode":"Test_TopUp","Price":{"CustomerFee":0.0,"DistributorFee":0.00,"ReceiveValue":245.0,"ReceiveCurrencyIso":"CUP","ReceiveValueExcludingTax":245.0,"TaxRate":0.0,"TaxName":null,"TaxCalculation":null,"SendValue":11.0,"SendCurrencyIso":"USD"},"CommissionApplied":0.00,"StartedUtc":"2025-09-09T14:10:57Z","CompletedUtc":"2025-09-09T14:10:58Z","ProcessingState":"Complete","ReceiptText":null,"ReceiptParams":null,"AccountNumber":"5350000055"},"ResultCode":1,"ErrorCodes":[]}.1756234923

Step 6: Verify the Signature

Using the public key from Step 1 and your preferred cryptographic library:

  1. Decode the signature from base64 to bytes
  2. Prepare verification:
    • Algorithm: RSA with PKCS#1 v1.5 padding and SHA-256 (RS256)
    • Input: The signed payload string from Step 5 (UTF-8 encoded)
    • Signature: The decoded signature bytes
    • Public key: Construct from the JWK n (modulus) and e (exponent) values
  3. Verify: Run the PKCS#1 v1.5 verification algorithm
  4. Result: If verification succeeds, the webhook is authentic

Step 7: Security Checks

Before processing the webhook:

  • ✅ Signature verification passed
  • ✅ Timestamp is recent (within tolerance)
  • ✅ Algorithm matches expected value (rs256)
  • ✅ Key ID exists in your cached JWKS

Example Verification Logic (Pseudocode)

1. GET public_keys FROM https://yourdomain.com/.well-known/webhook-keys
2. PARSE signature_header TO get timestamp AND signature_base64
3. VERIFY timestamp IS within 5 minutes of current_time
4. CREATE signed_payload = timestamp + "." + raw_request_body
5. DECODE signature_base64 TO signature_bytes
6. FIND public_key WHERE kid MATCHES header_key_id
7. VERIFY_RSA_SIGNATURE(signed_payload, signature_bytes, public_key, "SHA256")
8. IF verification_successful THEN process_webhook ELSE reject_request

Security Best Practices

Always verify signatures: Don't rely on HTTPS alone. Signature verification ensures the webhook actually came from Ding.

Use constant-time comparison: When comparing signatures, use timing-safe comparison functions to prevent timing attacks.

Implement replay protection: Always check timestamps and optionally track processed webhook IDs.

Handle errors securely: Return appropriate HTTP status codes:

  • 200 - Webhook received and verified successfully
  • 400 - Malformed signature or missing headers
  • 401 - Signature verification failed
  • 408 - Timestamp too old (replay protection)

Cache wisely: Cache public keys but refresh them regularly. Individual keys can be cached indefinitely by their kid, but the overall JWKS should be refreshed every few hours.

Common Pitfalls

  • Parsing JSON first: Don't parse the request body as JSON before verification
  • Wrong timestamp: Using current time instead of the timestamp from the header
  • Base64 vs Base64URL: Make sure you're using standard base64, not base64url
  • Algorithm assumptions: Always verify the algorithm matches what you expect
  • Stale keys: Not refreshing the JWKS periodically

Testing Your Implementation

You can test your verification logic using these known values:

  • Timestamp: 1756234923
  • Payload: {"test": true}
  • Expected signed string: 1756234923.{"test": true}

If your implementation is correct, verification should succeed with the corresponding public key from the JWKS endpoint.

Need Help?

If you're having trouble with signature verification:

  1. Verify you're using the raw request body
  2. Check that timestamps match exactly
  3. Ensure you're using the correct public key (matching kid)
  4. Confirm your RSA verification implementation supports PKCS#1 v1.5 with with SHA-256

For additional security considerations and best practices, see our general webhook security guide.

Webhook Encryption (Optional)

Ding supports optional payload encryption for webhooks, providing an additional layer of security beyond HTTPS and signature verification. When enabled, webhook payloads are encrypted using hybrid encryption before being sent to your endpoint.

Overview

Webhook encryption uses hybrid encryption combining symmetric and asymmetric cryptography for optimal security and performance:

  • Webhook payloads are encrypted using AES-256-GCM (fast symmetric encryption)
  • The AES encryption key is encrypted using RSA-OAEP with SHA-256 (secure asymmetric encryption)
  • You decrypt the AES key using your RSA private key, then decrypt the payload using the AES key
  • This provides end-to-end encryption for sensitive webhook data with better performance than pure RSA

When to Use Encryption

Consider enabling webhook encryption when:

  • Your webhook endpoint is publicly accessible and you want additional payload protection
  • You need to comply with strict data protection regulations
  • You want defense-in-depth security for sensitive transaction data
  • Your infrastructure requires encrypted data at rest and in transit

Note: Encryption is optional and complements (does not replace) signature verification. You should still verify webhook signatures even when using encryption.

Setting Up Encryption

Step 1: Generate RSA Key Pair

Generate a 2048-bit or 4096-bit RSA key pair. Here are examples for different platforms:

OpenSSL (Command Line):

# Generate private key
openssl genrsa -out webhook_private.pem 2048

# Extract public key
openssl rsa -in webhook_private.pem -pubout -out webhook_public.pem

PowerShell (.NET):

# Generate key pair
$rsa = [System.Security.Cryptography.RSA]::Create(2048)

# Export public key (PEM format)
$publicKeyPem = $rsa.ExportRSAPublicKeyPem()

# Export private key (PEM format) - store securely!
$privateKeyPem = $rsa.ExportRSAPrivateKeyPem()
Step 2: Configure Public Key in DingConnect

In your DingConnect account:

  1. Navigate to Settings > Developer > Optional feature settings > Transaction Webhook
  2. In the Webhook Encryption section, paste your RSA/X.509 public key in PEM format
  3. Click Validate to verify the key format
  4. Use the Test button to send a sample encrypted webhook
  5. Click Save to enable encryption

Important: Once you save a public key, all future webhooks will be encrypted. Ensure your endpoint can decrypt payloads before enabling this feature.

Step 3: Store Private Key Securely

Protect your private key:

  • Store in a secure key management system (AWS KMS, Azure Key Vault, etc.)
  • Use environment variables or secure configuration files
  • Never commit private keys to source control
  • Implement key rotation policies
  • Restrict access to the private key

Handling Encrypted Webhooks

Encryption Headers

Encrypted webhooks include additional headers:

Header Description Example
X-Ding-Webhook-Encryption Indicates hybrid encryption is used hybrid
X-Ding-Webhook-Data-Algorithm Symmetric encryption algorithm for payload AES-256-GCM
X-Ding-Webhook-Key-Algorithm Asymmetric encryption algorithm for key RSA-OAEP-SHA256
X-Ding-Webhook-Encrypted-Key RSA-encrypted AES key (base64 encoded) GApguZf6z+eQIbYdi8X...
X-Ding-Webhook-IV Initialization vector for AES-GCM (base64 encoded) /iZqj+FITPXdPtBV
Decryption Process

To decrypt a hybrid-encrypted webhook payload:

  1. Check encryption headers to confirm hybrid encryption is used
  2. Base64 decode the encrypted key from X-Ding-Webhook-Encrypted-Key
  3. Decrypt the AES key using your RSA private key and RSA-OAEP-SHA256
  4. Base64 decode the IV from X-Ding-Webhook-IV
  5. Decrypt the payload using AES-256-GCM with the decrypted key and IV
  6. Parse the decrypted JSON to get the original webhook payload

Complete Webhook Processing

When processing hybrid-encrypted webhooks, follow this order:

  1. Check encryption status using the X-Ding-Webhook-Encryption header
  2. Extract encryption metadata from headers (encrypted key, IV, algorithms)
  3. Decrypt the payload using hybrid decryption (examples above)
  4. Verify the signature using the decrypted payload (see signature verification section)
  5. Process the webhook business logic

Example processing flow:

1. READ request headers
2. IF X-Ding-Webhook-Encryption == "hybrid" THEN
3.     encrypted_payload = request.body
4.     encrypted_key = X-Ding-Webhook-Encrypted-Key header
5.     iv = X-Ding-Webhook-IV header
6.     decrypted_payload = decrypt_hybrid_payload(encrypted_payload, encrypted_key, iv, private_key)
7. ELSE
8.     decrypted_payload = request.body
9. END IF
10. VERIFY signature using decrypted_payload
11. PROCESS webhook business logic

Security Best Practices

Key Management:

  • Use 2048-bit or 4096-bit RSA keys
  • Rotate encryption keys periodically
  • Store private keys in secure key management systems
  • Use different keys for different environments (dev, staging, production)

Implementation:

  • Always verify signatures even with encrypted payloads
  • Implement proper error handling for decryption failures
  • Validate encryption headers before attempting decryption
  • Log encryption/decryption operations for monitoring
  • Test hybrid decryption thoroughly before production deployment

Monitoring:

  • Monitor decryption success/failure rates
  • Alert on decryption errors
  • Track webhook processing latency (encryption adds minimal overhead with AES)
  • Monitor for unexpected encryption algorithm changes

Troubleshooting

Common Issues:

  • Wrong RSA key format: Ensure you're using PEM format for the public key
  • Key mismatch: Verify the private key corresponds to the uploaded public key
  • Incorrect RSA padding: Use RSA-OAEP with SHA-256, not PKCS#1 v1.5
  • Base64 decoding errors: Ensure proper base64 decoding before decryption
  • AES-GCM tag handling: Handle authentication tag correctly (typically last 16 bytes)
  • IV reuse: Each webhook uses a unique IV - don't cache or reuse IVs
  • Character encoding: Use UTF-8 encoding for the decrypted JSON string

Testing:

  • Use the Test button in the DingConnect portal to send sample encrypted webhooks
  • Verify your hybrid decryption process with the test payload
  • Test signature verification with decrypted payloads
  • Validate error handling for malformed encrypted data
  • Test with different payload sizes to ensure consistent decryption

Performance Considerations

Hybrid encryption provides excellent performance characteristics:

  • Fast decryption: AES-256-GCM is highly optimized for bulk data
  • Small overhead: Only the AES key (32 bytes) is encrypted with RSA
  • Scalable: Performance doesn't degrade with larger payloads
  • Secure: Combines the security of RSA with the speed of AES

Disabling Encryption

To disable webhook encryption:

  1. Go to Settings > Developer > Webhooks
  2. Clear the public key field
  3. Click Save

After disabling encryption, webhooks will be sent as plain JSON (but still signed for verification).

Migration Strategy

When enabling encryption on existing webhooks:

  1. Implement hybrid decryption logic in your webhook handler
  2. Test thoroughly in a development environment
  3. Deploy decryption support to production (but don't enable encryption yet)
  4. Enable encryption in the DingConnect portal
  5. Monitor for any processing issues

This approach ensures your webhook handler can process both encrypted and unencrypted payloads during the transition.

Algorithm Details

For reference, the hybrid encryption uses:

  • Data Encryption: AES-256-GCM with 96-bit IV and 128-bit authentication tag
  • Key Encryption: RSA-OAEP with SHA-256 hash function and MGF1 mask generation
  • Encoding: All encrypted values are base64 encoded for HTTP transport
  • Key Size: 256-bit (32 bytes) randomly generated AES key per webhook

Responses

All requests and responses are formatted using the JSON interchange format https://www.json.org/. Parsers are available for all modern programming and scripting languages and are generally very easy to use. This format was chosen because it allows us to add new fields to our responses and to evolve our API over time without breaking conforming parsers.

You should not attempt to manually parse responses using regular expressions as extra fields can be added over time and there is every chance that the ordering of fields may change over time due to 3rd party framework and library upgrades. However a conforming parser will seamlessly handle these changes and your integration with our API will not break.

Every API response will contain a high level integer result code that broadly indicates the success, failure or otherwise of the actual call.

These are the top level ResultCode values that you can receive:

ResultCode HTTP Status Meaning Description
1 200 Success. The API call was processed successfully
2 200 Success With Warning. The API call was processed successfully, but there was a warning. Use the ErrorCodes for further details on the warning.
3 503/429 Transient Error. The API call failed because of some transient error. The request can be retried later. You can retry but should contact Ding if the error persists. There will be a Retry-After HTTP header indicating when to retry the request.
4 400/401 Client Error. The submitted request contained bad parameters. Do not retry with the same parameters. The caller must fix the input parameters before retrying.
5 500 Server Error. The parameters for the request are correct, but the server was unable to process them. Do not retry with the same parameters. You must contact Ding to resolve the situation.

In cases where the response does not succeed, the ErrorCodes array will contain one or more string error codes that indicate why the request failed.

You can use the GetErrorCodeDescriptions API call to get human readable error messages for each string in the ErrorCodes array.

Note that the top level set of ResultCode values will never change. However, we will continue to add more ErrorCodes on an ongoing basis. It would be dangerous to assume that the list of possible error codes will not change.

A full list of codes is listed below in the Error and Context Codes section below

Reference Data

The API tries to minimize the amount of data returned in each call. To do this, we use codes as much as possible. For example, we use standard country & currency ISO codes. We also have our own codes for Products and Providers.

There are API calls for retrieving the list of items and their code.

GetProviders will contain a list of Provider objects which, in turn, will contain its ProviderCode.

This code is then used in the definition of a Product.

Then GetProducts will contain a list of objects that each have a SkuCode.

This SkuCode is used in SendTransfer.

The idea with the various Get methods is that you should be able to cache the results in your application and you will rarely need to call them again. This kind of caching will greatly speed up the performance of your application by avoiding repeated, unnecessary calls to the server. But your application should not cache forever: instead it should honor the Cache-Control headers that are returned in the HTTP response headers. Many programming languages already have libraries that can interpret these headers correctly and we recommend their use.

Localization

In addition to reference data codes, we try to separate language-specific descriptions from the objects to which they apply. In some cases a description can be quite long and it may perhaps contain formatting information.

Products can have long descriptions.

Promotions can have detailed terms and conditions.

Each of these textual descriptions can be for multiple languages.

Each item that can be localized will have a LocalizationKey property. This localization key is used to find the full text appropriate to that object in the corresponding item Descriptions API call.

GetProductDescriptions is used to lookup localizations for products.

GetPromotionDescriptions is used with promotions.

Localizations will be returned in multiple languages. There will always be English (en) translations. Not all items will have translations in all languages, so when filtering by languageCode you should always request the en language as well as the intended target language.

Note that large blocks of text should be parsed as CommonMark. This will allow you to apply your own styling.

Again, the localization data is highly cacheable and you should load the localizations for your languages at intervals. Again, please pay attention to the cache control headers and refresh your cache when appropriate.

Querying

Many of the APIs return lists of items that can be filtered using parameters. For optimal usage, you should understand how parameters in the same request interact.

For all APIs, submitting no filter parameters will mean that all items will be returned.

Almost all filter parameters are arrays. This means that you can submit multiple values for the given parameter and the API will find items that have any of those values. In database terms, for a particular named parameter, all values provided are OR'd.

For the GetProducts API, you could submit countryIsos of [JM,HT]

The API method will return all products that have a countryIso of HT or JM.

It is possible to submit values for different named parameters in combination. The API will try to find items that satisfy all of those combinations. In database terms, with different named parameters the values are ANDed.

For the GetProducts API, you could submit countryIsos of [JM] and a benefits value of [Data].

The API method will return products that have both a country of JM and the Data benefit.

You can combine this effect in a powerful way by combining multiple values for different named parameters.

For the GetProducts API, you could submit countryIsos of [JM,HT] and benefits of [Data,Utility].

This would return all products that have Data or Utility benefits in Jamaica or Haiti.

Paging

In some of our API methods the number of items returned are bounded.

There are a limited number of countries and currencies in the world and we don't mind giving you that entire list.

However in some of our APIs the number of items returned could be unbounded and may return many items.

The number of transfers that you have done last month could number in the thousands.

For unbounded lists, we will not return the entire list. Instead we will return pages of data and you must request each page.

With unbounded lists, you can use Skip and Take parameters to indicate how many records to skip from the beginning of the list and how many items to take.

Skip=0 will skip no items in the list, and so represents the start of the list.

Skip=10 will skip ten items and so the eleventh item will be the first one returned.

Skip=0, Take=10 will start at the beginning of the list and return 10 items.

Skip=10, Take=10 will skip the first 10 items and return the next 10 items. This would be items 11-20

In this manner you can page through data. The API indicates when there are more items available by setting ThereAreMoreItems=true. When this property is false, then there is no more data. If you fetch another page, it will be empty.

Note that Take has a maximum of 100 and we will only allow you to Skip a maximum of 500 records. This allows a maximum of 600 records to be retrieved.

Batching

Some of our API methods support batching. This is the ability to submit multiple input items where each input item is processed individually. Each input item can succeed or fail independently and so the response needs to be able to report on the success or failure of each one.

Each batch request will contain an array of input items that are POSTed in the body of the request. Each of these items must contain a string BatchItemRef property that will uniquely identify the item within the overall request.

The response will contain a list of items, each of which will contain individual BatchItemRef, ResultCode and ErrorCodes properties as well as any other properties that are relevant to the particular method you called.

The BatchItemRef is the unique code you submitted in the input item. The ResultCode and ErrorCodes are the result and errors for that particular input item. If the ResultCode is success (see Responses above), then other properties in the result item will be populated. If the ResultCode is not success, then you should process the ErrorCodes array for this input item.

In this way, we can report on the success or failure of individual input items.

It is important to realize that, just like other API methods, there is still an overall success or failure for the overall request. If the overall request has failed, then none of the individual input items will have been processed. Only if the overall request has succeeded should the individual items be considered. Note that it is entirely possible for the overall request to succeed, even if all the input items failed.

Please note that the ordering of the items in the response list is not guaranteed to be in the same order as you submitted.

Method usage limit

Please note that for every successful SendTransfer the limits for each method will be reset to their original value.

Method Requests/10 minutes
get /api/V1/GetErrorCodeDescriptions 20
get /api/V1/GetCurrencies 20
get /api/V1/GetRegions 20
get /api/V1/GetCountries 20
get /api/V1/GetProviders 50
get /api/V1/GetProviderStatus 50
get /api/V1/GetProducts 50
get /api/V1/GetProductDescriptions 50
post /api/V1/SendTransfer Unlimited
post /api/V1/SendTransfer (Validate Only) 100
post /api/V1/EstimatePrices 50
get /api/V1/GetBalance 50
get /api/V1/GetPromotions 50
get /api/V1/GetPromotionDescriptions 50
post /api/V1/ListTransferRecords 50
post /api/V1/CancelTransfers 50
get /api/V1/GetAccountLookup 50

Terminology

  • Product-- An item that can be purchased and sent to an account number. Each product has a minimum and maximum price range and an identifying SkuCode.
  • Provider-- An organization that products can be purchased from. Providers trade in a single country and are assigned a unique ProviderCode. Providers can have an identical ShortName in different countries, but they are legally different entities. Many of our providers are mobile phone operators.
  • Region-- Each country can have a number of smaller regions. A provider may only service a subset of regions. A product may only be valid for a subset of regions.
  • Account Number-- This number identifies the account with the provider that will benefit from the purchase of the product. This is most typically a phone number, but not always. For some products and providers, this may not be numeric (it could be an e-mail address or a subscriber identifier).
  • Benefit-- Each product can convey a number of benefits on an account number, including mobile phone credit, data, bundles, sms, long distance credit, and so forth.
  • Transfer-- The purchase of a product, often described as a "top up" or a "recharge". Sending a transfer to an account number asks that the provider confer all of the benefits of a particular product to a specific account number.

Error and Context Codes

This is the current set of possible error codes and their descriptions. In some cases, the Context field will contain additional details or a secondary error code, which are explained here. It is important to always check the ResultCode field in the response, regardless of the HTTP status code. While HTTP status codes provide a general indication of the outcome (e.g., 200 for success or 503 for service unavailable), the ResultCode conveys the application-level meaning of the response and should be used to determine how to handle it. For example, a ResultCode of 3, which indicates a transient error, may be returned along with an HTTP 503 status. This typically means the operation failed due to a temporary issue, and a retry may succeed. When an error occurs, the response will also include an ErrorCode, a Description, and an optional Context field to provide more specific details about the error and assist in diagnosing the problem.

For a full mapping of ResultCode to HTTP status codes, see the Responses section.


Result Code ErrorCode Description Context
2 NearestMatch The submitted request could not be fulfilled exactly, so the system returned the nearest valid result. The context code will indicate why the request could not be fulfilled as submitted.
Code Description
ProviderCannotSupplyRequestReceiveValue An EstimatePrices request was submitted with a receive value, that the provider cannot supply. For example some providers may only be able to process whole amounts and a decimal receive value was submitted. The estimated price that was returned is for the nearest whole value.
3 RateLimited Too many simultaneous requests were attempted. Please reduce your request rate. Can also occur if a fraud rule is breached for an account number.
Code Description
TransactionStillInProgress A similar transaction is already in progress
TransientProviderError A temporary error occurred with the provider. Please try again later. If this persists, please contact support.
Code Description
ProviderTimedOut Communication timeout with the provider. Please try again later. If this persists, please contact support.
ProviderRefusedRequest The provider refused the request. This may be transient, but could be because the target account is not eligible for the chosen product.
ProviderTemporarilyUnavailable Provider currently unavailable, please try again later. If this persists, please contact support.
ProductUnavailable The send amount is currently unavailable, please try with a different send amount. If this persists, please contact support.
AccountTemporarilyUnavailable The target account is temporarily blocked by the provider, please try again later.
4 AccountNumberInvalid The submitted AccountNumber parameter failed validation.
Code Description
ProviderRefusedRequest The provider refused the request. This may be transient, but could be because the target account is not eligible for the chosen product.
AccountNumberFailedRegex The format of the account number failed regex validation. Please ensure the account number validates against the regex before submitting.
AuthenticationFailed Invalid or missing authentication credentials.
Code Description
TokenExpired The OAuth token has expired.
BatchItemRefMustBeUnique A duplicate BatchItemRef was used when submitting requests to a batch method.
ParameterCombinationInvalid The combination of parameters submitted to the method are not permitted. A comma-separated list of parameter names
ParameterInvalid One or more request parameters were invalid. A comma-separated list of parameter names
ParameterMissing One or more request parameters were missing. A comma-separated list of parameter names
ParameterOutOfRange One or more request parameters contained values that were outside the permitted range. A comma-separated list of parameter names
RequestInvalid The request was invalid. Please consult the method documentation for correct parameter format.
Code Description
MultipleRecordsMatched This context code can occur during the Cancel API and indicates that you submitted parameters that matched more than one transfer record and we did not know which one to cancel.
InstantTransfersCannotBeCancelled An attempt was made to cancel instant transfers. Only batch transfers can be cancelled.
NoRecordsFound No records were found that could be cancelled.
TooManyParameters One or more request parameters were submitted with too many values. A comma-separated list of parameter names
5 InsufficientBalance There is insufficient distributor balance available to service this request. Please contact finance.
OtherError The request failed due to an unclassified reason.
ProviderError An error occured with the provider.
Code Description
ProviderUnknownError An unknown error occured while communicating with the provider.
ProviderRefusedRequest The provider refused the request. This may be transient, but could be because the target account is not eligible for the chosen product.
ProviderTemporarilyUnavailable Provider currently unavailable, please try again later. If this persists, please contact support.
ProductUnavailable The send amount is currently unavailable, please try with a different send amount. If this persists, please contact support.
RechargeNotAllowed The selected product/send amount is not valid for the phone number provided, please try with a different product/send amount. If this persists, please contact support.

Optional Features

Prevent Duplicates

This is a feature to to reject SendTransfer requests with a DistributorRef already used for a SendTransfer request either in progress or a has been Successful in the past 60 minutes.

In the case the DistributorRef was already used, you will receive HTTP 400 with ResultCode of 4 and the error Code "DuplicateTransactionPrevented"