Authentication

Authentication

Secure your API requests with API keys and understand permission scopes.

API Keys

Harpoon uses API keys to authenticate requests. You can create and manage API keys from your dashboard.

Key Types

There are two types of API keys designed for different use cases:

TypePrefixUsageBest For
Secret Keyhpn_*_sk_Full API access. Server-side only.Backend servers, webhooks, reconciliation
Public Keyhpn_*_pk_Limited access. Safe for browser use.Frontend apps, mobile apps, checkout pages
Secret Key (sk)

Full API access for server-side operations. Never expose in browser code.

Can:

  • • Initialize payments with custom webhooks
  • • List all transactions
  • • Reconcile payments manually
  • • Configure webhook endpoints
Public Key (pk)

Browser-safe key for frontend integrations. Limited permissions.

Can:

  • • Initialize payments (no custom webhook)
  • • Check individual transaction status

Cannot:

  • • Set custom webhook URLs
  • • List all transactions
  • • Access reports or sensitive data

Using API Keys

Include your API key in the Authorization header as a Bearer token:

Plain Text
Authorization: Bearer hpn_live_sk_your_api_key_here

Example Request

Bash
curl -X POST https://api.harpoonsms.com/v1/transactions/initialize 
  -H "Authorization: Bearer hpn_live_sk_xxxxxxxxxxxxx" 
  -H "Content-Type: application/json" 
  -d '{
    "amount": "100.00",
    "description": "Test payment"
  }'

Permission Scopes

API keys can be configured with specific scopes to limit their permissions. This follows the principle of least privilege - only grant the permissions your integration needs.

ScopeDescriptionBrowser Safe
transactions:readRead transaction status (single transaction lookup)Yes
transactions:initializeInitialize payments only (no custom webhooks)Yes
transactions:writeFull transaction access: initialize, list, reconcileNo
webhooks:manageCreate, update, delete, and test webhook endpointsNo

Default Scopes

When creating keys, default scopes are assigned based on key type:

Key TypeDefault Scopes
Secret Key (sk)transactions:read, transactions:write
Public Key (pk)transactions:read, transactions:initialize

Public Key Restrictions

Public keys can only have browser-safe scopes (transactions:read, transactions:initialize). Attempting to assign other scopes to a public key will result in an error.

Scope Errors

If your API key doesn’t have the required scope for an operation, you’ll receive a 403 Forbidden response:

JSON
{
  "error": "Forbidden",
  "message": "API key does not have transactions:write scope"
}

IP Whitelisting

For additional security, you can restrict API access to specific IP addresses or CIDR ranges. Configure IP whitelisting from your API keys dashboard.

Supported formats:

  • Single IPv4 address: 192.168.1.100
  • CIDR range: 192.168.1.0/24

When IP whitelisting is enabled, requests from non-whitelisted IPs will receive a 403 Forbidden response:

JSON
{
  "error": "Forbidden",
  "message": "IP address not whitelisted"
}

Rate Limiting

API requests are rate limited to protect the service and ensure fair usage. Rate limits are applied per API key.

Key TypeDefault Limit
Secret Key (sk)100 requests/minute
Public Key (pk)100 requests/minute

When you exceed the rate limit, you’ll receive a 429 Too Many Requests response:

JSON
{
  "error": "Too Many Requests",
  "message": "Rate limit exceeded. Please try again later."
}

The response includes headers to help you manage your request rate:

  • X-RateLimit-Limit: Your rate limit per minute
  • X-RateLimit-Remaining: Requests remaining in current window
  • X-RateLimit-Reset: Unix timestamp when the limit resets

Creating API Keys

Via Dashboard

  1. Go to Settings → API Keys
  2. Click “Create API Key”
  3. Enter a name and select the environment
  4. Choose the required scopes
  5. Click “Create” and save the key immediately (it won’t be shown again)

Important

The full API key is only shown once when created. Store it securely immediately - you won't be able to retrieve it again.

Browser Integration

Public keys are designed for browser/frontend integrations. Here’s how to use them safely:

Basic Browser Example

JavaScript
// Use a PUBLIC key - safe to expose in browser code
const HARPOON_PUBLIC_KEY = 'hpn_live_pk_your_key_here';

async function initializePayment(amount, phone) {
  const response = await fetch('https://api.harpoonsms.com/v1/transactions/initialize', {
    method: 'POST',
    headers: {
      'Authorization': `Bearer ${HARPOON_PUBLIC_KEY}`,
      'Content-Type': 'application/json'
    },
    body: JSON.stringify({
      amount: amount,
      currency: 'GHS',
      phone_number: phone,
      description: 'Order payment'
      // Note: webhook_url is NOT allowed with public keys
    })
  });

  return response.json();
}

// Poll for payment status
async function checkPaymentStatus(reference) {
  const response = await fetch(`https://api.harpoonsms.com/v1/transactions/${reference}`, {
    headers: { 'Authorization': `Bearer ${HARPOON_PUBLIC_KEY}` }
  });

  return response.json();
}

Receiving Verifications

When using public keys, you have two options for receiving payment verifications:

  1. Polling - Check transaction status periodically using transactions:read
  2. Pre-configured Webhooks - Set up webhooks via the dashboard (they’ll fire for all payments)
JavaScript
// Option 1: Polling for status
async function waitForPayment(reference, maxWaitMs = 300000) {
  const startTime = Date.now();

  while (Date.now() - startTime < maxWaitMs) {
    const result = await checkPaymentStatus(reference);

    if (['SUCCESS', 'PARTIAL', 'OVERPAID'].includes(result.data.status)) {
      return { success: true, data: result.data };
    }
    if (['FAILED', 'EXPIRED', 'CANCELLED'].includes(result.data.status)) {
      return { success: false, data: result.data };
    }

    await new Promise(resolve => setTimeout(resolve, 3000)); // Poll every 3s
  }

  throw new Error('Payment verification timed out');
}

Public keys cannot set custom webhook URLs for security reasons. Configure webhooks via the dashboard or with a secret key.

Revoking API Keys

If you suspect a key has been compromised, revoke it immediately:

  1. Go to Settings → API Keys
  2. Find the key you want to revoke
  3. Click the delete/revoke button
  4. Confirm the revocation

Revoked keys are immediately invalidated. Any requests using the revoked key will receive a 401 Unauthorized response.

Next Steps