Seev Business API

Errors

Understand Seev API error codes, HTTP status codes, and how to handle them gracefully.

The Seev API uses conventional HTTP status codes and returns a consistent JSON error envelope on every failure. This page explains the error format, lists all error codes, and covers best practices for handling errors gracefully in your integration.

Error response format

All API errors return a JSON body with the same shape:

{
  "status": "error",
  "code": "INVALID_API_KEY",
  "message": "The provided API key is invalid or has been revoked.",
  "param": null
}
FieldTypeDescription
status"error"Always "error" for failure responses.
codestringMachine-readable error code. Use this for programmatic handling.
messagestringHuman-readable description. Suitable for logs — not for end-user display.
paramstring | nullThe specific request field that caused the error, if applicable.

HTTP status codes

StatusMeaning
200Success
400Bad request — invalid or missing parameters
401Unauthenticated — missing or invalid API key
403Forbidden — the key is valid but lacks permission for this action
404Not found — the resource does not exist
409Conflict — duplicate request (check Idempotency)
422Unprocessable — request is well-formed but semantically invalid
429Rate limited — too many requests
500Internal server error — something went wrong on Seev's end

Error codes

Authentication

CodeStatusDescription
INVALID_API_KEY401The API key is missing, malformed, or revoked.
KEY_MISMATCH401The public key and secret key belong to different accounts.
FORBIDDEN403The key does not have permission for the requested operation.

Request validation

CodeStatusDescription
MISSING_PARAM400A required parameter was not provided. param contains the field name.
INVALID_PARAM400A parameter was provided but has an invalid value or type.
INVALID_AMOUNT400The amount value is zero, negative, or non-numeric.
INVALID_CURRENCY400The currency code is not supported.
INVALID_PAYLOAD400The request body could not be parsed (e.g. malformed JSON or webhook body).

Payments

CodeStatusDescription
CHARGE_FAILED422The payment was declined by the processor.
CHARGE_EXPIRED422The payment session timed out before the customer completed the flow.
DUPLICATE_CHARGE409A charge with the same idempotency key already exists.
INVALID_SESSION404The checkout session ID is not found or has already been used.
REFUND_EXCEEDS_AMOUNT422The refund amount is greater than the original charge or remaining refundable balance.
ALREADY_REFUNDED409The charge has already been fully refunded.

Webhooks

CodeStatusDescription
INVALID_SIGNATURE401The webhook signature does not match — the payload may have been tampered with or the wrong secret is being used.
MISSING_EVENT_TYPE400The webhook body does not contain an event field.

Infrastructure

CodeStatusDescription
RATE_LIMITED429You have exceeded the request rate limit. Back off and retry after the Retry-After header value.
NOT_FOUND404The requested resource does not exist.
INTERNAL_ERROR500An unexpected error occurred on the Seev API. Retry with exponential backoff.
NETWORK_ERROR(SDK only) The request never reached the API — check connectivity.

Handling errors with the payment gateway

When using Seev.charge() to create a checkout session, errors generally fall into two categories:

1. Validation errors (before the customer sees anything) These happen when your server calls Seev.charge() with invalid parameters. Catch them and respond to your user with a meaningful message — do not redirect to the checkout URL.

2. Payment errors (after the customer completes the flow) These surface when you call Seev.callback() or Seev.transaction() to verify the result. A CHARGE_FAILED or CHARGE_EXPIRED code means the payment did not go through — show an error and let the customer try again.

Example

import Seev from '@seev/sdk';

// --- Creating a charge ---
let checkoutUrl: string;

try {
  const charge = await Seev.charge({
    type: 'checkout',
    amount: 5000,
    currency: 'GHS',
    recipient: { name: 'Jane Doe', email: 'jane@example.com' },
  });
  checkoutUrl = charge.redirect_url;
} catch (error) {
  if (error instanceof Error) {
    // Log the raw message for debugging
    console.error('[Seev] Charge failed:', error.message);

    // Return a generic message to the client — never expose raw API errors
    return res.status(500).json({ error: 'Could not initiate payment. Please try again.' });
  }
}

// --- Verifying the result after redirect ---
try {
  const result = await Seev.callback(req.body);

  if (result.status === 'success') {
    // Fulfil the order
  } else {
    // Payment declined — prompt the user to retry
    return res.status(400).json({ error: 'Payment was not completed.' });
  }
} catch (error) {
  if (error instanceof Error) {
    console.error('[Seev] Callback error:', error.message);
    return res.status(400).json({ error: 'Invalid payment callback.' });
  }
}

Best practices

Never display raw API error messages to users

The message field is intended for logs and developers. It may contain internal details that are confusing or misleading to end users. Map error codes to friendly copy in your UI instead.

Use code for programmatic handling

Always branch on code, not message. The message may change across API versions; the code is stable.

switch (errorCode) {
  case 'CHARGE_FAILED':
    return 'Your payment was declined. Please try a different payment method.';
  case 'CHARGE_EXPIRED':
    return 'Your payment session timed out. Please start again.';
  default:
    return 'Something went wrong. Please try again or contact support.';
}

Retry 5xx errors with exponential backoff

INTERNAL_ERROR (500) and RATE_LIMITED (429) are transient. Retry with increasing delays — for example, 1s, 2s, 4s — with a maximum of 3–4 attempts. Do not retry 4xx errors as they indicate a problem with the request itself.

Log the full error context

Include the error code, message, param, and the relevant resource ID (e.g. transaction reference) in your logs so you can correlate failures in your monitoring tools.

Handle webhook signature failures silently

If Seev.webhook() throws an INVALID_SIGNATURE error, return a 200 response anyway — do not return 401 or 400. Returning a non-2xx status will cause Seev to retry the delivery, which can amplify noise from malformed requests. Just discard the event and log the failure.

On this page