info@marcbrainltd.com +233 2487 70024

Apps

Marcbrain Marcbrain

Automations  /  SwiftClaim  /  Documentation

SwiftClaim V3

Developer documentation: webhook setup, app configuration, and SMS pattern customization.

What is SwiftClaim?

SwiftClaim is an Android app that runs silently in the background and monitors your SMS inbox for mobile money payment notifications. When a matching payment SMS is detected, it extracts the transaction data and HTTP POSTs it as JSON to your webhook URL - in real time.

You receive clean, structured data and do whatever you want with it update a database, credit a wallet, trigger an alert, mark an order as paid, and more.

No polling needed. SwiftClaim pushes data to you the moment a matching SMS arrives, so your system reacts instantly without running scheduled checks.

How It Works

The flow is straightforward:

Phone receives SMS → SwiftClaim detects it → Parses fields → POSTs JSON to your webhook URL

The app runs as a persistent foreground service - similar to how WhatsApp or Google Maps keeps running in the background. It survives app close, screen off, and phone reboot.

Version History

V1 Deprecated

  • Basic SMS reading capability
  • Requires a paid API key to function
  • Manual trigger only , no automatic detection
  • High battery drain due to aggressive polling

V2 Legacy

  • Resolved battery drain - no longer aggressively polls
  • Removed payment requirement - free to use
  • Still manual trigger only
  • No persistent background service

V3 Current

  • Supports both manual triggers and automatic foreground/background running
  • Persistent notification (non-dismissible) confirms the service is always active
  • Survives app close, screen off, and device reboot
  • Requires a licensed App Key. key is locked to one device on first use
  • History tab to review past detections and failed webhook deliveries

Installation & App Key

SwiftClaim V3 requires a valid App Key before it can process any SMS. Each key is unique per customer and locks to a single device on first use.

1

Download & Install the APK

Download the latest SwiftClaim V3 APK from your order confirmation email or the SwiftClaim page . Install it on your Android device (enable Install from unknown sources if prompted).

APK install screen
2

Enter Your App Key

On first launch, the app will prompt you to enter your App Key. Paste the key exactly as provided - it is case-sensitive. The key is verified against the Marcbrain server and then bound permanently to your device ID.

Device Lock: Once activated, your App Key cannot be used on a second device. If you change phones, contact the app administrator to unbind the old device.
App Key entry screen
3

Grant SMS Permissions

SwiftClaim requires permission to read SMS messages. When prompted, tap Allow. Without this permission the app cannot detect incoming payment messages.

SMS permission dialog

Configure Webhook URL

Your webhook URL is the HTTPS endpoint on your server where SwiftClaim will POST payment data. You must set this before the app can send anything.

1

Open Settings

In the app, tap the Settings icon (top-right or bottom navigation, depending on your version).

Settings button location in SwiftClaim app
2

Enter Your Webhook URL

Find the Webhook URL field. Paste your full HTTPS endpoint, for example:

URL https://yourdomain.com/webhook.php

You can also add a secret token as a query parameter for added security:

URL https://yourdomain.com/webhook.php?token=your_secret_token

Tap Save

Tap Save. The app will confirm the URL is stored. You can test delivery using the Send Test button (if available) or by triggering a manual scan.

Webhook URL input field in Settings
3

Tap Save

Tap Save. The app will confirm the URL is stored. You can test delivery using the Send Test button (if available) or by triggering a manual scan.

SMS Pattern Settings

SwiftClaim uses a regex pattern to identify and extract data from payment SMS messages. A default pattern is included, but you can customize it for your bank or mobile money provider.

Default Pattern

The default pattern matches messages in this format:

SMS Sample Payment received for GHS 310.00 from RICHMOND OBENG Current Balance: GHS 558.92 . Available Balance: GHS 558.92. Reference: 1. Transaction ID: 79864960076. TRANSACTION FEE: 0.00

Default regex:

Regex Payment received for GHS ([\d,.]+) from (.+?)\s{2,}Current Balance: GHS ([\d,.]+)\s*\.\s*Available Balance: GHS ([\d,.]+)\s*\.\s*Reference:\s*(\S+)\s*\.\s*Transaction ID:\s*(\d+)\s*\.\s*TRANSACTION FEE:\s*([\d,.]+)

Capture groups (in order): amount, from, current_balance, available_balance, reference, transaction_id, transaction_fee.

Custom Pattern

To set a custom pattern, go to Settings → SMS Pattern, clear the default regex, and paste your own. Make sure your regex has the same 7 capture groups in the same order, or the extracted data will be misaligned.

Tip: Test your regex on regex101.com using a real sample SMS before saving it in the app.
Pattern Generation Tool

Foreground Service & Persistence

SwiftClaim V3 runs as a persistent foreground service. This means:

  • A permanent notification appears in the status bar confirming the service is active (this notification cannot be dismissed that is intentional)
  • The service survives screen off, app close, and phone reboot
  • No manual trigger is needed - SMS are detected automatically as they arrive

You can also trigger a manual scan from the app's main screen at any time to process any unprocessed messages in your inbox.

Battery: The foreground service approach is optimized for minimal battery use. Unlike V1, SwiftClaim V3 reacts to incoming SMS events rather than polling on a timer.
Persistent notification in status bar

Payload Reference

When a payment SMS is detected, SwiftClaim sends a POST request to your webhook URL with the following JSON body:

JSON { "event": "payment_received", "timestamp": "2025-10-14T09:45:00.000Z", "data": { "amount": "310.00", "from": "RICHMOND OBENG", "current_balance": "558.92", "available_balance": "558.92", "reference": "1", "transaction_id": "79864960076", "transaction_fee": "0.00" } }
Field Type Description
event string Always "payment_received"
timestamp string ISO 8601 datetime of when the SMS was detected
data.amount string Amount received (GHS), e.g. "310.00"
data.from string Sender name as it appears in the SMS
data.current_balance string Account current balance after transaction
data.available_balance string Account available balance after transaction
data.reference string Payment reference number provided by sender
data.transaction_id string Unique transaction ID from the network — use this to detect duplicates
data.transaction_fee string Fee charged for the transaction

Webhook Examples

Your webhook must be a publicly accessible HTTPS URL that accepts POST requests with Content-Type: application/json and returns HTTP 200 on success. Choose your language below:

PHP
<?php
// webhook.php

header('Content-Type: application/json');

// Optional: validate secret token
$token = $_GET['token'] ?? '';
if ($token !== 'your_secret_token') {
    http_response_code(403);
    echo json_encode(['status' => 'forbidden']);
    exit;
}

// Read raw payload
$payload = json_decode(file_get_contents('php://input'), true);

if (!$payload || ($payload['event'] ?? '') !== 'payment_received') {
    http_response_code(400);
    echo json_encode(['status' => 'error', 'message' => 'Invalid payload']);
    exit;
}

$data = $payload['data'] ?? [];

$amount         = $data['amount'] ?? null;
$from           = $data['from'] ?? null;
$txn_id         = $data['transaction_id'] ?? null;
$current_bal    = $data['current_balance'] ?? null;
$reference      = $data['reference'] ?? null;
$fee            = $data['transaction_fee'] ?? null;

// TODO: Check for duplicate transaction
// e.g. SELECT COUNT(*) FROM payments WHERE transaction_id = '$txn_id'

// TODO: Your logic here
// e.g. insert into DB, credit wallet, notify staff, mark order paid

// Log to file
file_put_contents(
    __DIR__ . '/webhook_log.txt',
    "[" . date("Y-m-d H:i:s") . "] GHS $amount from $from | TxnID: $txn_id" . PHP_EOL,
    FILE_APPEND
);

http_response_code(200);
echo json_encode(['status' => 'received']);
Node.js / Express
const express = require('express');
const app = express();
app.use(express.json());

app.post('/webhook', (req, res) => {
  // Optional: validate secret token
  const token = req.query.token;
  if (token !== 'your_secret_token') {
    return res.status(403).json({ status: 'forbidden' });
  }

  const { event, timestamp, data } = req.body;

  if (event !== 'payment_received') {
    return res.status(400).json({ status: 'error', message: 'Invalid event' });
  }

  const {
    amount,
    from,
    transaction_id,
    current_balance,
    reference,
    transaction_fee
  } = data;

  // TODO: check duplicate by transaction_id
  // TODO: your logic — update DB, credit wallet, etc.

  console.log(`[${timestamp}] GHS ${amount} from ${from} | TxnID: ${transaction_id}`);

  res.status(200).json({ status: 'received' });
});

app.listen(3000, () => console.log('Webhook server running on port 3000'));
Python / Flask
from flask import Flask, request, jsonify
import logging

app = Flask(__name__)
logging.basicConfig(level=logging.INFO)

SECRET_TOKEN = 'your_secret_token'

@app.route('/webhook', methods=['POST'])
def webhook():
    # Optional: validate secret token
    token = request.args.get('token', '')
    if token != SECRET_TOKEN:
        return jsonify({'status': 'forbidden'}), 403

    payload = request.get_json(silent=True)

    if not payload or payload.get('event') != 'payment_received':
        return jsonify({'status': 'error', 'message': 'Invalid payload'}), 400

    data = payload.get('data', {})

    amount       = data.get('amount')
    from_name    = data.get('from')
    txn_id       = data.get('transaction_id')
    balance      = data.get('current_balance')
    reference    = data.get('reference')
    fee          = data.get('transaction_fee')
    timestamp    = payload.get('timestamp')

    # TODO: check duplicate by txn_id
    # TODO: your logic — update DB, credit wallet, etc.

    logging.info(f"[{timestamp}] GHS {amount} from {from_name} | TxnID: {txn_id}")

    return jsonify({'status': 'received'}), 200

if __name__ == '__main__':
    app.run(port=5000)
Laravel (PHP)
// routes/api.php
        Route::post('/webhook/swiftclaim', [SwiftClaimWebhookController::class, 'handle']);
        
        // ---
        
        // app/Http/Controllers/SwiftClaimWebhookController.php
        namespace App\Http\Controllers;
        
        use Illuminate\Http\Request;
        use App\Models\Payment;
        use Illuminate\Support\Facades\Log;
        
        class SwiftClaimWebhookController extends Controller
        {
            public function handle(Request $request)
            {
                // Optional: validate secret token
                if ($request->query('token') !== config('services.swiftclaim.secret')) {
                    return response()->json(['status' => 'forbidden'], 403);
                }
        
                $payload = $request->json()->all();
        
                if (empty($payload) || ($payload['event'] ?? '') !== 'payment_received') {
                    return response()->json(['status' => 'error'], 400);
                }
        
                $data = $payload['data'] ?? [];
        
                // Prevent duplicate processing
                if (Payment::where('transaction_id', $data['transaction_id'])->exists()) {
                    return response()->json(['status' => 'duplicate'], 200);
                }
        
                // Save to DB
                Payment::create([
                    'amount'           => $data['amount'],
                    'from'             => $data['from'],
                    'transaction_id'   => $data['transaction_id'],
                    'current_balance'  => $data['current_balance'],
                    'reference'        => $data['reference'],
                    'transaction_fee'  => $data['transaction_fee'],
                    'received_at'      => now(),
                ]);
        
                Log::info('SwiftClaim payment received', $data);
        
                return response()->json(['status' => 'received'], 200);
            }
        }
        
        // ---
        
        // Don't forget to exclude this route from CSRF in:
        // app/Http/Middleware/VerifyCsrfToken.php
        // protected $except = ['api/webhook/swiftclaim'];
WordPress (functions.php or plugin)
// Add to your theme's functions.php or a custom plugin file.
        // Registers a REST API endpoint:
        // POST https://yourdomain.com/wp-json/swiftclaim/v1/webhook
        
        add_action('rest_api_init', function () {
            register_rest_route('swiftclaim/v1', '/webhook', [
                'methods'             => 'POST',
                'callback'            => 'swiftclaim_handle_webhook',
                'permission_callback' => '__return_true',
            ]);
        });
        
        function swiftclaim_handle_webhook(WP_REST_Request $request) {
            // Optional: validate secret token
            $token = $request->get_param('token');
            if ($token !== 'your_secret_token') {
                return new WP_REST_Response(['status' => 'forbidden'], 403);
            }
        
            $payload = $request->get_json_params();
        
            if (empty($payload) || ($payload['event'] ?? '') !== 'payment_received') {
                return new WP_REST_Response(['status' => 'error'], 400);
            }
        
            $data = $payload['data'] ?? [];
        
            $amount      = sanitize_text_field($data['amount'] ?? '');
            $from        = sanitize_text_field($data['from'] ?? '');
            $txn_id      = sanitize_text_field($data['transaction_id'] ?? '');
            $balance     = sanitize_text_field($data['current_balance'] ?? '');
            $reference   = sanitize_text_field($data['reference'] ?? '');
            $fee         = sanitize_text_field($data['transaction_fee'] ?? '');
            $timestamp   = sanitize_text_field($payload['timestamp'] ?? '');
        
            // Check for duplicate
            global $wpdb;
            $table = $wpdb->prefix . 'swiftclaim_payments';
            $exists = $wpdb->get_var(
                $wpdb->prepare("SELECT COUNT(*) FROM $table WHERE transaction_id = %s", $txn_id)
            );
            if ($exists > 0) {
                return new WP_REST_Response(['status' => 'duplicate'], 200);
            }
        
            // Insert into custom table
            $wpdb->insert($table, [
                'amount'           => $amount,
                'sender'           => $from,
                'transaction_id'   => $txn_id,
                'current_balance'  => $balance,
                'reference'        => $reference,
                'transaction_fee'  => $fee,
                'received_at'      => current_time('mysql'),
            ]);
        
            // Optional: send admin email
            // wp_mail(get_option('admin_email'), 'New Payment via SwiftClaim', "GHS $amount from $from");
        
            return new WP_REST_Response(['status' => 'received'], 200);
        }
        
        // ---
        
        // Create DB table on plugin activation:
        function swiftclaim_create_table() {
            global $wpdb;
            $table = $wpdb->prefix . 'swiftclaim_payments';
            $charset = $wpdb->get_charset_collate();
            $sql = "CREATE TABLE IF NOT EXISTS $table (
                id bigint(20) NOT NULL AUTO_INCREMENT,
                amount varchar(20) NOT NULL,
                sender varchar(100) NOT NULL,
                transaction_id varchar(50) NOT NULL UNIQUE,
                current_balance varchar(20),
                reference varchar(50),
                transaction_fee varchar(20),
                received_at datetime DEFAULT CURRENT_TIMESTAMP,
                PRIMARY KEY (id)
            ) $charset;";
            require_once(ABSPATH . 'wp-admin/includes/upgrade.php');
            dbDelta($sql);
        }
        register_activation_hook(__FILE__, 'swiftclaim_create_table');

Security Tips

  • Use a secret token, append a token to your URL (?token=abc123) and validate it on every request. Reject any call that doesn't match.
  • Validate transaction_id, always check if you've already processed this ID before taking action. The app may retry a failed delivery, and you never want to credit the same payment twice.
  • Use HTTPS only. SwiftClaim will only send to HTTPS endpoints. Never use a plain HTTP URL.
  • Respond with 200 quicklydo heavy processing (DB writes, email notifications) asynchronously or in a queue. A slow response may cause the app to treat the delivery as failed.
  • Log all incoming requests keep a raw log of payloads (see the PHP example above) so you can debug and replay if needed.
Never expose your webhook URL publicly without a secret token. Anyone who knows the URL can POST fake payment data to your endpoint.

Retry Behavior

  • If your webhook is unreachable or returns a non-200 status, SwiftClaim marks the record as failed in local history.
  • Failed entries are not automatically retried, this is intentional to avoid duplicate processing on your end.
  • You can review failed entries in the app's History tab and manually resend if needed.
Best practice: Build your webhook to be idempotent: i.e., processing the same transaction_id twice should have no harmful side effect. This protects you if a manual resend occurs.

Support & Contact

Built and maintained by MarcBrain Ltd.