Salami Gateway

API Documentation
Back to Dashboard

Error Codes Reference

Complete reference for HTTP status codes, error response formats, module-specific error codes, and troubleshooting guidance.


HTTP status codes

Salami uses standard HTTP status codes. The table below lists every code you may encounter.

Code Name When it occurs
200 OK Request succeeded. Response body contains the result.
201 Created Resource created successfully (e.g. new token, new item registration).
400 Bad Request Malformed request syntax, missing required headers, or unparseable JSON body.
401 Unauthorized Missing Authorization header, invalid token, expired token, or revoked token.
403 Forbidden Token is valid but lacks the required scope, or the authenticated user does not have permission.
404 Not Found The requested resource (payment app, transaction, SMS app, item, etc.) does not exist or does not belong to the authenticated user.
422 Unprocessable Entity Validation failed. The request body was well-formed JSON but contained invalid data. See the errors object for field-level details.
429 Too Many Requests Rate limit exceeded (200 requests per minute per token). Back off and retry.
500 Internal Server Error Unexpected server error. Contact support with the request_id if available.
502 Bad Gateway Upstream provider (KRA, M-Pesa, Airtel, SMS gateway) returned an error. The error field contains details from the provider.
503 Service Unavailable Salami or a downstream provider is temporarily unavailable. Retry with exponential backoff.

Standard error response format

Basic error

{
  "success": false,
  "error": "Human-readable error message"
}

Validation error (422)

{
  "success": false,
  "error": "Validation failed",
  "errors": {
    "KRAPIN": ["The KRAPIN field is required."],
    "amount": ["The amount must be greater than 0."]
  }
}

The errors object is keyed by field name. Each value is an array of validation messages for that field.

Validation error with missing fields (KRA module)

{
  "success": false,
  "error": "Missing required fields for PIN check",
  "missing_fields": ["KRAPIN"]
}

Provider error (502)

{
  "success": false,
  "error": "KRA returned an error: Invalid PIN format",
  "response_code": "E003"
}

The response_code field is only present when Salami can extract a structured error code from the upstream provider.


Payments error codes

General payment errors

Error HTTP Code Cause Resolution
Payment app not found 404 The {payment_app} ID in the URL does not exist or does not belong to the authenticated user Verify the app ID in your dashboard under Payments > Payment Apps
Payment app inactive 403 The app exists but is disabled Enable the app in the dashboard
Transaction not found 404 The {transactionId} or {transaction} does not exist for this app Check the transaction ID; it may belong to a different app
Validation failed 422 Required fields missing or invalid (phone, amount, reference, etc.) Fix the fields listed in the errors object
Provider error 500 The payment provider returned an unexpected error Check the error message, retry, or contact support

M-Pesa result codes

These codes come directly from Safaricom and appear in callback payloads and status-check responses.

Result Code Description User action
0 Success Transaction completed. No action needed.
1 Insufficient balance Customer does not have enough funds. Inform them to top up.
1019 Transaction limit exceeded Customer's per-transaction or daily limit reached. Reduce the amount or try later.
1032 Request cancelled by user Customer dismissed the STK Push prompt. Retry or use a different payment method.
1037 Timeout waiting for user input Customer did not respond to the STK Push within ~30 seconds. Retry the request.
2001 Invalid MSISDN The phone number is not a valid Safaricom number. Verify the format (254...).
2006 Invalid initiator credentials Consumer Key, Consumer Secret, or Passkey is wrong. Update credentials in the dashboard.
404012 Invalid shortcode or account reference The shortcode or account number is not registered. Verify in the dashboard.
500.001.1001 Duplicate request A transaction with the same parameters was already submitted. Use a unique reference.
400.008.02 Invalid receiver The B2C recipient phone number is invalid or not registered.

Airtel Money error codes

Error Description Resolution
Invalid PIN Customer entered incorrect Airtel Money PIN Customer should retry with correct PIN
Insufficient balance Customer or merchant balance too low Top up before retrying
KYC verification required Customer has not completed KYC Customer must complete Airtel Money KYC verification
Limit exceeded Daily or per-transaction limit reached Reduce amount or wait for the next day

MTN Mobile Money error codes

Error Description Resolution
Payer not found The phone number is not registered for MTN MoMo Verify the phone number
Insufficient balance Payer does not have enough funds Customer must top up
Duplicate request A transaction with the same external reference exists Use a unique reference for each request

Equity Bank error codes

Code Description Resolution
EB001 Invalid account number Verify the bank account number
EB002 Insufficient funds Top up the merchant account
EB003 Account suspended Contact Equity Bank support
EB004 Daily transaction limit exceeded Increase limits with the bank or wait for the next day

SMS error codes

Send errors

Error HTTP Code Cause Resolution
SMS app not found 404 Invalid {sms_app} ID Check the app ID in your dashboard
SMS app inactive 403 App is disabled Re-enable the app
Invalid phone number 422 Phone number is not in valid international format Use E.164 format: +254712345678
Empty message 422 The message field is empty or missing Provide a non-empty message string
Message too long 422 Message exceeds 1600 characters Split into multiple messages
Invalid recipients 422 All phone numbers in the request are invalid Fix the recipient list
Insufficient credits 402 SMS account balance too low Top up SMS credits in the dashboard
Invalid sender ID 422 Sender ID is not approved by the carrier Use a registered sender ID or the default phone number
Blocked number 422 Recipient has opted out or is on a blocklist Remove this number from your list
Provider error 500/502 The SMS provider returned an error Check the error message and retry

SMS delivery status codes

These statuses appear in outbox queries and delivery report callbacks.

Status Description Is final?
queued Message accepted and queued for sending No
sent Message dispatched to the carrier network No
delivered Carrier confirmed delivery to the handset Yes
failed Sending failed (network error, invalid number, provider rejection) Yes
rejected Carrier rejected the message (spam filter, invalid sender, etc.) Yes
expired Carrier could not deliver within the validity period (phone off, out of range) Yes

KRA Portal error codes

KRA ResponseCode values

When Salami proxies a request to the KRA developer.go.ke APIs, KRA returns a ResponseCode in its response. Salami maps failures to HTTP 502 and includes the code in response_code.

ResponseCode Meaning Resolution
00 Success Request processed successfully
01 Invalid credentials Client ID or Client Secret is wrong. Update the KRA app credentials.
02 Token expired KRA OAuth token expired mid-request. This is auto-retried by Salami; if persistent, re-test the app.
03 Unauthorized scope Your KRA app does not have access to this API on the developer portal
E001 Invalid request payload Required fields are missing or malformed
E002 Taxpayer not found The KRA PIN does not exist in the KRA database
E003 Invalid PIN format PIN does not match the expected pattern
E004 Service temporarily unavailable KRA backend is down or overloaded
E005 Rate limit exceeded Too many requests to the KRA API
E010 Certificate not found TCC, import certificate, or excise licence number does not exist
E011 Declaration not found The customs declaration number does not exist

KRA validation errors

These are returned as HTTP 422 before the request reaches KRA.

Field Rule Example error
KRAPIN Required, exactly 11 characters The KRAPIN must be 11 characters.
TaxpayerID Required string The TaxpayerID field is required.
TaxpayerType Must be KE or FR The selected TaxpayerType is invalid.
invoiceNumber Required string The invoiceNumber field is required.
invoiceDate Required, format YYYY-MM-DD The invoiceDate does not match the format Y-m-d.
tccNumber Required string The tccNumber field is required.
DeclarationNo Required string The DeclarationNo field is required.
ObligationCode Must be 1 through 8 The selected ObligationCode is invalid.
Month Exactly 2 characters (01-12) The Month must be 2 characters.
Year Exactly 4 characters The Year must be 4 characters.

eTIMS OSCU error codes

eTIMS resultCd values

KRA's eTIMS OSCU system returns a resultCd in every response. 000 means success; anything else is an error.

resultCd Meaning Resolution
000 Success Request processed and accepted by KRA
001 Invalid request Request structure is malformed. Check required fields.
002 Authentication failed TIN, BHFID, or device serial is wrong. Re-initialize the device.
003 Device not initialized The OSCU device has not been registered with KRA
004 Device not authorized The device is registered but not authorized for this operation
005 Duplicate invoice An invoice with this number has already been submitted
006 Item not found The item code referenced in the sale does not exist in KRA's registry
007 Invalid tax calculation Tax amounts do not match the expected values for the given tax type and rate
008 Invalid date format Date field not in the expected format
009 Invalid customer TIN Customer TIN is not registered with KRA
010 Stock insufficient Attempted to sell more than available stock
011 Invalid receipt type Receipt type code is not valid
012 Server error KRA eTIMS backend encountered an internal error
013 Service maintenance eTIMS is undergoing scheduled maintenance

eTIMS validation errors (Salami-side)

These are returned as HTTP 422 before the request is forwarded to KRA.

Field Rule Example error
tin Required string The tin field is required.
bhf_id Required string The bhf_id field is required.
dvc_srl_no Required string The dvc_srl_no field is required.
date Required, valid date The date field is required.
trader_invoice_number Required string The trader_invoice_number field is required.
receipt_type_code Must be S, P, or T The selected receipt_type_code is invalid.
sales_tax_summary Required array The sales_tax_summary field is required.
sales_tax_summary.taxable_amount_a Required numeric The sales_tax_summary.taxable_amount_a field is required.
sales_tax_summary.tax_rate_b Required numeric The sales_tax_summary.tax_rate_b field is required.
sales_tax_summary.tax_amount_b Required numeric The sales_tax_summary.tax_amount_b field is required.
customer_tin Optional string (validated if present) The customer_tin must be a string.
callback_url Optional, must be valid URL The callback_url must be a valid URL.

eTIMS tax type codes

Code Name Rate
A Excise Duty 0% (exempt)
B VAT (standard) 16%
C VAT (zero-rated) 0%
D Non-VAT (exempt) 0%
E VAT (tourism levy) 2%

eTIMS payment type codes

Code Description
01 Cash
02 Credit
03 Cheque
04 Bank transfer
05 Mobile money
06 Other

General API errors

These apply across all modules.

Error HTTP Code Cause Resolution
Unauthenticated. 401 No Bearer token or token is invalid/expired/revoked Include a valid Authorization: Bearer <token> header
Token expired 401 The token's expires_at date has passed Create a new token
Insufficient permissions 403 Token does not have the required scope for this endpoint Create a token with the appropriate scope (see Authentication)
kra_app_id is required 422 KRA endpoint called without specifying an app Add X-KRA-App-Id header or kra_app_id parameter
Resource not found 404 The ID in the URL does not match any record Verify the resource exists and belongs to your account
Method not allowed 405 Wrong HTTP method (e.g. GET on a POST-only endpoint) Check the API reference for the correct method
Too many requests 429 Exceeded 200 requests/minute for this token Implement exponential backoff and request caching
Internal server error 500 Unhandled exception on the server Contact support@dgl.co.ke with the request details
Bad gateway 502 Upstream provider (KRA, M-Pesa, etc.) returned an error Check the error and response_code fields. Retry if transient.
Service unavailable 503 Salami or a provider is temporarily down Retry with backoff. Check https://status.salami.dgl.co.ke

Rate limiting

Limit Value
Requests per minute per token 200
HTTP code when exceeded 429 Too Many Requests
Retry header Retry-After (seconds until the window resets)

When you receive a 429, wait for the number of seconds indicated in the Retry-After header before retrying.


Troubleshooting flowchart

Request failed
  |
  +-- HTTP 401?
  |     Check Authorization header. Is the token correct, not expired, not revoked?
  |
  +-- HTTP 403?
  |     Check token scopes. Does the token have the scope required by this endpoint?
  |
  +-- HTTP 404?
  |     Does the resource (app ID, transaction ID, item code) exist and belong to you?
  |
  +-- HTTP 422?
  |     Read the `errors` object. Fix the validation issues and retry.
  |
  +-- HTTP 429?
  |     You are sending too many requests. Wait, then retry with backoff.
  |
  +-- HTTP 500?
  |     Server bug. Log the full response and contact support@dgl.co.ke.
  |
  +-- HTTP 502?
  |     Upstream provider error. Check `error` and `response_code`.
  |     Is the provider down? Retry after a delay.
  |
  +-- HTTP 503?
        Service unavailable. Check status.salami.dgl.co.ke. Retry later.

Error handling best practices

1. Always check the success field

$response = json_decode($httpResponse->getBody(), true);

if ($response['success'] === true) {
    $data = $response['data'];
    // Process successful response
} else {
    $error = $response['error'] ?? $response['message'] ?? 'Unknown error';
    Log::error('Salami API error', [
        'error' => $error,
        'response_code' => $response['response_code'] ?? null,
        'errors' => $response['errors'] ?? null,
    ]);
}

2. Implement exponential backoff for transient errors

async function callWithRetry(fn, maxRetries = 3) {
  for (let attempt = 0; attempt < maxRetries; attempt++) {
    try {
      return await fn();
    } catch (error) {
      const status = error.response?.status;
      const isRetryable = [429, 500, 502, 503].includes(status);

      if (!isRetryable || attempt === maxRetries - 1) {
        throw error;
      }

      const delay = Math.pow(2, attempt) * 1000; // 1s, 2s, 4s
      await new Promise(resolve => setTimeout(resolve, delay));
    }
  }
}

3. Map error codes to user-friendly messages

USER_MESSAGES = {
    401: "Your session has expired. Please log in again.",
    403: "You do not have permission to perform this action.",
    422: "Please check your input and try again.",
    429: "Too many requests. Please wait a moment.",
    500: "Something went wrong. Please try again later.",
    502: "The payment provider is temporarily unavailable. Please retry.",
    503: "Service is under maintenance. Please try again shortly.",
}

def get_user_message(status_code, error_detail=None):
    return USER_MESSAGES.get(status_code, "An unexpected error occurred.")

4. Log everything

For every failed API call, log:

This makes debugging with Salami support significantly faster.


Getting help

  1. Check this reference -- most errors are documented above.
  2. Review the endpoint documentation -- ensure your request matches the expected format.
  3. Test with curl -- isolate the issue from your application code.
  4. Check the status page -- https://status.salami.dgl.co.ke for outage information.
  5. Contact support -- email support@dgl.co.ke with the error response, request details, and timestamps.

Back to documentation home


Need help? Contact us at support@dgl.co.ke
© 2026 Deadan Group Limited. All rights reserved.
⚡ API Explorer
LIVE
// Response will appear here...