Skip to main content
Shiipp uses two authentication schemes: JWT Bearer tokens for all standard API endpoints, and API key authentication for the public prealert submission endpoint used by courier integrations. This page covers how to obtain and use both credential types, how to handle the optional two-factor authentication flow, and what error responses to expect when authentication fails.

Obtaining a JWT Token

Send a POST request to /api/login.php with your Shiipp username and password. On success, the response contains an access_token that must be included in the Authorization header of every subsequent request. Endpoint: POST /api/login.php

Request Body

action
string
required
Must be the literal string "login".
username
string
required
Your Shiipp account username.
password
string
required
Your Shiipp account password.

Example Login Request

curl -X POST https://your-domain.com/api/login.php \
  -H "Content-Type: application/json" \
  -d '{
    "action": "login",
    "username": "your_username",
    "password": "your_password"
  }'

Success Response (No 2FA)

{
  "status": "success",
  "message": "Login successful",
  "data": {
    "access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
    "token_type": "Bearer",
    "user": {
      "id": "user-uuid",
      "full_name": "Jane Smith",
      "username": "jsmith",
      "role": "courier",
      "courier_id": "courier-uuid",
      "courier_code": "JLC",
      "two_factor_enabled": false
    }
  }
}

Using the Token

Include the token in the Authorization header for every subsequent API call. Tokens expire after 8 hours. When your token expires, repeat the login flow to obtain a new one — there is no refresh token endpoint.
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
Store the token in a secure location such as an environment variable, a secrets manager, or an encrypted session store. Never commit tokens to source control.

Login Response Fields

access_token
string
The JWT token to use in the Authorization: Bearer header.
token_type
string
Always "Bearer".
user
object
Profile information for the authenticated user.

Two-Factor Authentication (2FA) Flow

If the account has 2FA enabled, the initial POST /api/login.php call returns a "2fa_required" status instead of an access_token. The response includes a short-lived preauth_token that acts as a challenge ticket.

Step 1 — Initial Login Response (2FA Required)

{
  "status": "2fa_required",
  "message": "Two-factor authentication required",
  "data": {
    "preauth_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
  }
}
The preauth_token is valid for 5 minutes. If it expires before the user submits their code, restart the flow from the initial login request.

Step 2 — Submit the TOTP Code

Make a second POST /api/login.php call with action: "verify_2fa", the preauth_token from step one, and the six-digit TOTP code from the user’s authenticator app.
action
string
required
Must be the literal string "verify_2fa".
preauth_token
string
required
The preauth_token returned in the 2fa_required response.
code
string
required
The six-digit TOTP code from the user’s authenticator app. Backup codes are also accepted in place of a TOTP code.
{
  "action": "verify_2fa",
  "preauth_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
  "code": "123456"
}
On success, the response is identical in shape to the standard login success — you receive a full access_token and user object. From this point the token is used identically regardless of whether 2FA was involved.
Backup codes work as a drop-in replacement for the TOTP code field. Prompt the user to use a backup code if they cannot access their authenticator app.

API Key Authentication

The prealert submission endpoint (POST /api/Prealert.php) is designed for server-to-server courier integrations and uses a static API key instead of a JWT token. This avoids the need to manage token expiry in automated pipelines. Obtain your API key from the Courier Settings section of the Shiipp dashboard. Each courier account has its own unique key.

Sending the API Key

The recommended method is to pass the key as a request header:
X-API-KEY: your_api_key
You can also pass it as a query parameter, though this is not recommended for production use because query strings are more likely to appear in server logs and browser history.
POST /api/Prealert.php?api_key=your_api_key
Avoid query-parameter authentication in production. Prefer the X-API-KEY header so your key is not captured in access logs, proxy caches, or browser history.

Authentication Error Reference

HTTP Codestatus FieldCause
400failThe request body is missing or contains invalid JSON
422failRequired fields (username or password) are absent or blank in the login request
401failInvalid username or password
401failInvalid or expired 2FA code
401errorJWT token is missing, malformed, or expired on a protected endpoint
403failAccount exists but has been disabled by an administrator