Salami Gateway uses Laravel Sanctum token-based authentication. Every API request (except provider callbacks and webhooks) must include a valid Bearer token in the Authorization header.
{id}|{random-string}.401/403.Authorization: Bearer 1|a8Bk3xY9zR4mNpQ7wXcV2bLsKjHgFdE...
Tokens are hashed before storage. The plain-text value is shown exactly once at creation time. If you lose it, revoke the old token and create a new one.
production-erp, staging-mobile-app).curl -X POST https://yourtenant.salami.dgl.co.ke/account/api_tokens \
-H "Content-Type: application/json" \
-H "Cookie: <your-session-cookie>" \
-H "X-CSRF-TOKEN: <csrf-token>" \
-d '{
"name": "my-server-token",
"abilities": "payments:read,payments:write,sms:write",
"expires_at": "2027-06-01"
}'
Response (success):
{
"success": true,
"token": "3|xK9mR4a8Bk3...plainTextToken",
"msg": "API token created successfully"
}
Parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
name |
string | Yes | Unique name for this token |
abilities |
string | No | Comma-separated list of scopes. Defaults to * (full access) |
expires_at |
date | No | ISO 8601 date (YYYY-MM-DD). Must be in the future. Null = never expires |
Scopes restrict what a token can do. Each scope maps to a set of API routes. A request to an endpoint that falls outside the token's scopes returns 403 Forbidden.
There are 17 scopes across four modules:
| Scope | Description | Endpoints granted |
|---|---|---|
payments:read |
View payment apps, transactions, balances, and statuses | GET /api/pay/apps, GET /api/pay/{app}/transaction/{id}, GET /api/pay/{app}/queryTransactions, GET /api/pay/{app}/checkBalance, GET /api/pay/{app}/fetchTransactions, GET /api/pay/{app}/getTransactionStatus/{txn}, GET /api/pay/{app}/checkPaymentRequestStatus/{txn}, GET /api/pay/{app}/getInstructions/{txn} |
payments:write |
Create and modify transactions | POST /api/pay/{app}/extractTransaction, POST /api/pay/{app}/registerUrls, POST /api/pay/{app}/requestPayment, POST /api/pay/{app}/reverseTransaction/{txn}, POST /api/pay/{app}/sendAirtime, POST /api/pay/{app}/sendMoney, POST /api/pay/{app}/simulateTransaction, ANY /api/pay/{app}/validateTransaction |
payments:callback |
Handle payment provider callbacks | ANY /api/pay/{app}/callback/{action?} |
| Scope | Description | Endpoints granted |
|---|---|---|
sms:read |
View SMS apps, messages, inbox, outbox, and call logs | GET /api/sms/apps/{app}, POST /api/sms/groups/{app}, GET /api/sms/apps/{app}/inbox, GET /api/sms/apps/{app}/outbox, GET /api/sms/apps/{app}/calls, GET /api/sms/sms/{id} |
sms:write |
Send SMS messages to individuals and groups | POST /api/sms/apps/{app}/send, POST /api/sms/groups/{app}/send |
sms:sync |
Synchronise SMS data from Android gateway apps | ANY /api/sms/msync, ANY /api/sms/smssync |
sms:receive |
Receive incoming SMS messages and delivery callbacks | ANY /api/sms/apps/{app}/receive, ANY /api/sms/apps/{app}/callback |
| Scope | Description | Endpoints granted |
|---|---|---|
etims:read |
View device info, sales, purchases, stock, items, code tables, notices, reports, customers, suppliers, credit notes, reverse invoices, verifications, and business info | 27 read-only eTIMS endpoints |
etims:write |
Submit sales, purchases, stock movements, items, customers, suppliers, credit notes, and reverse invoices; initialize device; acknowledge notices | 16 write eTIMS endpoints |
etims:callback |
Handle eTIMS/KRA webhook callbacks | ANY /api/kra/etims/webhook/callback |
| Scope | Description | Endpoints granted |
|---|---|---|
kra:apps |
List and test KRA app configurations | GET /api/kra/apps, POST /api/kra/apps/{id}/test |
kra:checkers |
PIN validation, TCC checks, invoice lookup, customs declarations, exemptions, tax service office | 13 checker endpoints under /api/kra/checkers/... |
kra:payments |
Generate Payment Reference Numbers for withholding taxes (IT, rental, VAT) | POST /api/kra/payments/it-withholding, POST /api/kra/payments/rental-withholding, POST /api/kra/payments/vat-withholding |
kra:compliance |
Apply for Tax Compliance Certificates | POST /api/kra/compliance/tcc-application |
kra:registration |
Register new KRA PINs | POST /api/kra/registration/pin |
kra:returns |
File TOT and NIL tax returns | POST /api/kra/returns/tot, POST /api/kra/returns/nil |
| Scope | Description |
|---|---|
* |
Full access to every endpoint. Use only for admin or internal tooling. |
Scope groups are pre-defined bundles for common use cases. Select a group in the dashboard to automatically check all its member scopes.
| Group key | Label | Included scopes |
|---|---|---|
payments_full |
Full Payments Access | payments:read, payments:write, payments:callback |
sms_full |
Full SMS Access | sms:read, sms:write, sms:sync, sms:receive |
etims_full |
Full eTIMS Access | etims:read, etims:write, etims:callback |
kra_full |
Full KRA Portal Access | kra:apps, kra:checkers, kra:payments, kra:compliance, kra:registration, kra:returns |
kra_read_only |
KRA Read Only | kra:apps, kra:checkers |
read_only |
Read Only (all modules) | payments:read, sms:read, etims:read, kra:apps, kra:checkers |
send_only |
Send Only | payments:write, sms:write |
full_access |
Complete Full Access | All 16 non-wildcard scopes |
Grant only what the token needs. Examples:
| Use case | Recommended scopes |
|---|---|
| Reporting dashboard | payments:read, sms:read, etims:read |
| Payment collection server | payments:read, payments:write, payments:callback |
| SMS marketing campaign | sms:write |
| POS system with eTIMS | etims:read, etims:write |
| KRA compliance tool | kra:checkers, kra:compliance |
| ERP full integration | full_access group |
KRA Portal endpoints require you to specify which KRA app to use on every request. This is because a single account can have multiple KRA apps for different taxpayer PINs or environments.
Pass the app ID using either method:
curl -X POST https://yourtenant.salami.dgl.co.ke/api/kra/checkers/pin \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "X-KRA-App-Id: 3" \
-H "Content-Type: application/json" \
-d '{"KRAPIN": "P051234567A"}'
curl -X POST https://yourtenant.salami.dgl.co.ke/api/kra/checkers/pin \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"kra_app_id": 3,
"KRAPIN": "P051234567A"
}'
If neither is provided, the API returns:
{
"success": false,
"error": "kra_app_id is required. Pass it as a parameter or X-KRA-App-Id header."
}
Payments and SMS modules use path-based app selection instead (
/api/pay/{payment_app}/...and/api/sms/apps/{sms_app}/...), so no extra header is needed.
expires_at date set at creation time.401 Unauthorized.expires_at via the dashboard or the update endpoint.Revoke a token instantly from the dashboard or programmatically:
curl -X POST https://yourtenant.salami.dgl.co.ke/account/api_tokens/5/revoke \
-H "Cookie: <your-session-cookie>" \
-H "X-CSRF-TOKEN: <csrf-token>"
{
"success": true,
"msg": "API token revoked successfully"
}
Revoked tokens return 401 on all subsequent requests.
Deleting a token permanently removes it:
curl -X DELETE https://yourtenant.salami.dgl.co.ke/account/api_tokens/5 \
-H "Cookie: <your-session-cookie>" \
-H "X-CSRF-TOKEN: <csrf-token>"
Every authenticated request needs at minimum:
curl https://yourtenant.salami.dgl.co.ke/api/pay/apps \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Accept: application/json"
curl -X POST https://yourtenant.salami.dgl.co.ke/api/pay/1/requestPayment \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
-H "Accept: application/json" \
-d '{
"phone": "254712345678",
"amount": 500,
"reference": "ORDER-12345",
"description": "Payment for order #12345"
}'
curl -X POST https://yourtenant.salami.dgl.co.ke/api/kra/payments/it-withholding \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
-H "X-KRA-App-Id: 2" \
-d '{
"transactionHeader": {
"withholderPin": "P051234567A",
"transactionUniqueNo": "WHT-2026-001",
"noOfTransactions": 1,
"taxPeriodFrom": "01/03/2026",
"taxPeriodTo": "31/03/2026",
"totalGrossAmount": 100000,
"totalTaxAmount": 5000
},
"transactionDetails": [
{
"payeePin": "P059876543B",
"grossAmount": 100000,
"taxAmount": 5000
}
]
}'
| Symptom | Cause | Fix |
|---|---|---|
401 Unauthorized |
Missing, malformed, expired, or revoked token | Check the Authorization header format. Create a new token if the old one is expired or revoked. |
403 Forbidden |
Token lacks the required scope | Check which scope the endpoint needs (see tables above) and create a token with that scope. |
422 kra_app_id is required |
KRA endpoint called without specifying an app | Add the X-KRA-App-Id header or kra_app_id body parameter. |
| Token not accepted after creation | Token string was truncated or had whitespace added during copy/paste | Ensure the full token (including the numeric prefix and pipe character) is used exactly as shown at creation time. |