v1.0.0 · REST · JSON

API Reference

Everything you need to integrate PingBacks into your hosting platform. Three core endpoints, JWT auth, HMAC-signed webhooks.

Base URL

https://api.pingbacks.io

All endpoints return JSON. Use Content-Type: application/json for POST/PUT requests.

Authentication

PingBacks supports two authentication methods — API Keys for server-to-server integration and JWT Bearer Tokens for user-facing dashboards.

1 API Key Authentication

Used by providers for all transactional endpoints (/earn, /balance, /redeem). Your API key is generated when your provider account is approved.

# Include in every request header:
X-PingBacks-API-Key: pb_live_8f391a2b3c4d5e6f7a8b9c0d1e2f3a4b

API keys are hashed with SHA-256 before storage. Never expose your key in client-side code or version control.

2 JWT Bearer Token

Used by provider dashboards and customer portals. Obtain a token via /api/auth/login/tenant or /api/auth/login/user. Tokens expire after 24 hours.

# Include in every request header:
Authorization: Bearer eyJhbGciOiJIUzI1NiIs...

Core Concept: Ping-ID

Every registered customer receives a unique Ping-ID (format: PB-XXXXXXXXXXXX). This is their loyalty card number — they enter it during checkout, and you send it with your API calls. No need to collect or store email addresses.

✓ With Ping-ID

// Customer enters Ping-ID at checkout
POST /api/v1/earn
{ "ping_id": "PB-A3F8K2M9X", ... }

Also Supported: Email

// Fallback for legacy integrations
POST /api/v1/earn
{ "customer_email": "kunde@example.de", ... }

Earning Points

POST
/api/v1/earn

Credit points to a customer after a successful payment. Idempotent — sending the same invoice_id twice returns a 409 Conflict.

Request Body

FieldTypeRequiredDescription
ping_idstring✓*Customer's Ping-ID (e.g. PB-A3F8K2M9X)
customer_emailstring*Customer's email (alternative to ping_id)
tenant_customer_idstringYour internal customer ID
invoice_idstringUnique invoice/order ID (idempotency key)
items[].categorystringProduct category: hosting, domain, server, addon
items[].amount_eurnumberAmount in euros (e.g. 49.90)

* Either ping_id or customer_email is required.

Example Request

curl -X POST https://api.pingbacks.io/api/v1/earn \
  -H "Content-Type: application/json" \
  -H "X-PingBacks-API-Key: pb_live_8f391a..." \
  -d '{
    "ping_id": "PB-A3F8K2M9X",
    "tenant_customer_id": "cust_12345",
    "invoice_id": "inv_99812",
    "items": [
      { "category": "hosting", "amount_eur": 49.90 },
      { "category": "domain",  "amount_eur": 14.90 }
    ]
  }'

Example Response 200 OK

{
  "status": "success",
  "pingbacks_user_id": "8040dd4c-6929-4741-8fd6-69386c308d2f",
  "points_earned": 648,
  "new_global_balance": 12850
}

Multiplier Calculation

Points are calculated as: floor(amount_eur × multiplier). Each provider configures their own multipliers per category. Example with multiplier 10: €14.90 → 149 points.

Query Balance

GET
/api/v1/balance

Look up a customer's current point balance. Use this to display points in your customer panel.

Query Parameters

ParameterTypeRequiredDescription
emailstring*Customer's email address
ping_idstring*Customer's Ping-ID (alternative)

Example Request

curl "https://api.pingbacks.io/api/v1/balance?ping_id=PB-A3F8K2M9X" \
  -H "X-PingBacks-API-Key: pb_live_8f391a..."

Example Response 200 OK

{
  "customer_email": "developer@agency-xyz.de",
  "ping_id": "PB-A3F8K2M9X",
  "global_balance": 12850,
  "status": "linked"
}

Status linked means the customer exists. not_found means no account with that email/Ping-ID.

Redeeming Points

POST
/api/v1/redeem/invoice

Redeem a customer's points to pay for an invoice. Deducts points from the global wallet. Triggers a signed webhook to the provider.

Request Body

FieldTypeRequiredDescription
ping_idstring✓*Customer's Ping-ID
customer_emailstring*Customer's email (alternative)
points_to_redeemintegerNumber of points to redeem (positive)
invoice_idstringInvoice ID this redemption applies to

Example Request

curl -X POST https://api.pingbacks.io/api/v1/redeem/invoice \
  -H "Content-Type: application/json" \
  -H "X-PingBacks-API-Key: pb_live_8f391a..." \
  -d '{
    "ping_id": "PB-A3F8K2M9X",
    "points_to_redeem": 1000,
    "invoice_id": "inv_99850"
  }'

Example Response 200 OK

{
  "status": "success",
  "points_redeemed": 1000,
  "equivalent_value_eur": 10.00,
  "remaining_global_balance": 11850
}

1 PingBack = €0.01. Redemption is atomic — either fully succeeds or fully fails. If the customer has insufficient points, returns 400 INSUFFICIENT_BALANCE.

Webhooks

When a customer redeems points at your store, PingBacks sends a signed HTTP POST to your configured webhook URL. This allows you to automatically credit the invoice in your billing system.

Webhook Payload

POST <your-webhook-url>
Content-Type: application/json
X-PingBacks-Signature: <HMAC-SHA256-hex>

{
  "event": "points_redeemed",
  "tenant_id": "5927c0df-0284-4f27-ba49-6b1264b788b4",
  "user_id": "1bebdfc0-2f3f-4233-a66c-706e8cb41060",
  "reference_id": "inv_99850",
  "points": 1000,
  "timestamp": "2026-07-25T12:00:00Z",
  "signature": "a1b2c3d4e5f6..."
}

Signature Verification

Verify webhooks by computing HMAC-SHA256 over the JSON body (excluding the signature field) using your webhook secret.

// Node.js
const crypto = require('crypto');

function verifyWebhook(body, signature, secret) {
  const payload = { event: body.event, tenant_id: body.tenant_id,
    user_id: body.user_id, reference_id: body.reference_id,
    points: body.points, timestamp: body.timestamp };
  const expected = crypto
    .createHmac('sha256', secret)
    .update(JSON.stringify(payload))
    .digest('hex');
  return crypto.timingSafeEqual(
    Buffer.from(signature), Buffer.from(expected)
  );
}
// PHP
function verifyWebhook($body, $signature, $secret) {
    $payload = json_encode([
        'event'        => $body['event'],
        'tenant_id'    => $body['tenant_id'],
        'user_id'      => $body['user_id'],
        'reference_id' => $body['reference_id'],
        'points'       => $body['points'],
        'timestamp'    => $body['timestamp'],
    ]);
    return hash_equals(
        hash_hmac('sha256', $payload, $secret),
        $signature
    );
}

Auth API

JWT-based authentication for provider dashboards and customer portals. All auth endpoints are public (no API key required).

POST
/api/auth/register/tenant

Register a new provider. Goes into pending approval state.

// Request
{
  "company_name": "ServerBlitz GmbH",
  "contact_email": "kontakt@serverblitz.de",
  "contact_name": "Max Mustermann",
  "admin_email": "admin@serverblitz.de",
  "admin_password": "secure1234",
  "webhook_url": "https://serverblitz.de/webhooks/pingbacks"
}

// Response 201 Created
{
  "status": "success",
  "message": "Registration submitted. Awaiting admin approval.",
  "tenant_id": "5927c0df-..."
}
POST
/api/auth/register/user

Register a new end customer. Returns JWT token immediately — no approval needed.

// Request
{ "email": "developer@agency-xyz.de", "password": "secure1234" }

// Response 201 Created
{
  "status": "success",
  "token": "eyJhbGciOi...",
  "user": {
    "id": "1bebdfc0-...",
    "email": "developer@agency-xyz.de",
    "current_balance": 0,
    "ping_id": "PB-A3F8K2M9X"
  }
}
POST
/api/auth/login/tenant

Login as a provider. Returns JWT token with tenant context.

// Response 200 OK
{
  "status": "success",
  "token": "eyJhbGciOi...",
  "user": {
    "email": "admin@serverblitz.de",
    "tenantId": "5927c0df-...",
    "tenantName": "ServerBlitz GmbH",
    "role": "admin"
  }
}
POST
/api/auth/login/user

Login as an end customer. Returns JWT token with wallet context.

// Response 200 OK
{
  "status": "success",
  "token": "eyJhbGciOi...",
  "user": {
    "email": "developer@agency-xyz.de",
    "current_balance": 12850,
    "ping_id": "PB-A3F8K2M9X"
  }
}

Dashboard API JWT Required

Authenticated endpoints for provider dashboards. All require Authorization: Bearer <token>.

GET /api/auth/tenant/stats

Returns provider info, transaction counts, 30-day points total, unique customer count, and current multipliers.

GET /api/auth/tenant/transactions?limit=100

Paginated transaction list for the authenticated provider. Includes customer email, points before/after, timestamp.

PUT /api/auth/tenant/multipliers

Update multipliers. Sends full replacement array. Body: { "multipliers": [{"category":"hosting","points_per_euro":10}] }

GET /api/auth/admin/registrations

List all provider registrations with status. For admin users approving new providers.

POST /api/auth/admin/approve

Approve or reject a provider registration. Body: { "tenant_id": "...", "approved": true }. Returns generated API key on approval.

GET /api/auth/user/transactions

End-customer transaction history with provider names. Returns current balance and Ping-ID.

Error Codes

All errors follow a consistent format: { "status": "error", "code": "ERROR_CODE", "message": "Human-readable description" }

HTTPCodeDescription
400INVALID_REQUESTMissing or invalid request fields
400INSUFFICIENT_BALANCECustomer doesn't have enough points for this redemption
400ZERO_POINTSNo points calculated — check category names and multipliers
400WEAK_PASSWORDPassword must be at least 6 characters
401MISSING_API_KEYX-PingBacks-API-Key header is missing
401INVALID_API_KEYAPI key is invalid or tenant is suspended
401UNAUTHORIZEDJWT Bearer token missing or invalid
401INVALID_CREDENTIALSWrong email or password
403FORBIDDENInsufficient permissions for this endpoint
404USER_NOT_FOUNDNo user found with that email or Ping-ID
409DUPLICATE_INVOICEThis invoice_id has already been processed
409EMAIL_EXISTSThis email is already registered
500INTERNAL_ERRORUnexpected server error — contact support

PHP SDK

Minimal, zero-dependency client for PHP 7.4+. Works with WHMCS, WordPress, WooCommerce, and any custom CMS.

<?php
require_once 'PingBacksClient.php';

$client = new PingBacksClient('pb_live_8f391a...', 'https://api.pingbacks.io');

// 1. Credit points after payment
$result = $client->earn(
    pingId: 'PB-A3F8K2M9X',
    tenantCustomerId: 'cust_12345',
    invoiceId: 'inv_99812',
    items: [
        ['category' => 'hosting', 'amount_eur' => 49.90],
        ['category' => 'domain',  'amount_eur' => 14.90],
    ]
);
echo "Earned: {$result['points_earned']} points\n";

// 2. Check balance
$balance = $client->getBalance(pingId: 'PB-A3F8K2M9X');
echo "Balance: {$balance['global_balance']} points\n";

// 3. Redeem points
$redeem = $client->redeemInvoice(
    pingId: 'PB-A3F8K2M9X',
    pointsToRedeem: 500,
    invoiceId: 'inv_789'
);
echo "Redeemed: {$redeem['points_redeemed']}\n";
echo "Remaining: {$redeem['remaining_global_balance']}\n";

Full SDK source and example script: github.com/Host-On/pingbacks/sdk/php

OpenAPI Specification

Import our OpenAPI 3.0 spec into Postman, Swagger, or Insomnia.

Download openapi.yaml