Authentication
TSPay API uses JWT (JSON Web Tokens) for secure, server-to-server authentication. API clients authenticate using credentials issued by TSPay to obtain short-lived access tokens used to authorize API requests.
Table of contents
- Overview
- Getting API Credentials
- Login
- Token Refresh
- Session Termination
- JWT Token Structure
- Using JWT Tokens
- Authentication vs Authorization
- Token Management Best Practices
- Security Considerations
- Common Authentication Errors
- Next Steps
Overview
All API endpoints except /api/v1/auth/login require authentication using a valid JWT access token in the Authorization header. This includes /api/v1/auth/refresh, which requires a currently valid (non-expired) access token.
The authentication flow is:
- Receive API credentials from TSPay (contact support)
- Login to receive a short-lived access token and a refresh token
- Include the token in the
Authorization: Bearer <token>header for all API requests - Refresh the access token using the refresh token before it expires
Authorization is handled server-side by TSPay. The access token identifies your credential — permissions associated with that credential are resolved internally on each request. A valid token is not sufficient if the associated credential has not been granted the required permission for the operation. Contact TSPay support to review your credential’s permission set.
This authentication mechanism secures server-to-server integrations between travel platforms and the Travelsoft Pay issuing infrastructure.
Getting API Credentials
Contact TSPay: TSPay provisions API credentials for each organisation. Contact TSPay support to obtain your API credentials (username and password). Your account will be pre-configured with the necessary issuing processor settings and permissions.
Sandbox and Production environments use different base URLs and credentials. Tokens issued in one environment cannot be used in the other.
| Environment | Base URL |
|---|---|
| Production | https://tspay-api.live.travelsoftpay.com |
| Sandbox | https://tspay-api.sandbox.travelsoftpay.com |
Login
Endpoint
POST /api/v1/auth/login
This endpoint does not require an Authorization header.
Request Body
| Field | Type | Required | Description |
|---|---|---|---|
username | string | Yes | Your API username (provided by TSPay) |
password | string | Yes | Your API password (provided by TSPay) |
Example Request
curl -X POST https://tspay-api.sandbox.travelsoftpay.com/api/v1/auth/login \
-H "Content-Type: application/json" \
-d '{
"username": "acme_corp",
"password": "SecureP@ssw0rd123!"
}'
Success Response (200 OK)
{
"accessToken": "eyJraWQiOi...",
"accessTokenExpiresAt": 1736515800,
"idToken": "eyJraWQiOi...",
"idTokenExpiresAt": 1736515800,
"refreshToken": "eyJjdHkiOi...",
"refreshTokenExpiresAt": 1736601600
}
All expiration fields are Unix timestamps (seconds since epoch).
| Field | Description |
|---|---|
accessToken | JWT access token — include in Authorization: Bearer for all API calls |
accessTokenExpiresAt | Unix timestamp when the access token expires (1 hour from issuance) |
idToken | OpenID Connect ID token |
refreshToken | Long-lived token used to obtain new access tokens |
refreshTokenExpiresAt | Unix timestamp when the refresh token expires (24 hours from issuance) |
The
idTokenis an OpenID Connect identity token. It is not required for most API operations and can typically be ignored unless your application needs to validate the authenticated user identity.
Error Response
{
"correlationId": "2aaa9f82-4873-4ba9-a0a3-e2228ff25078",
"status": 401,
"message": "Authentication failed",
"details": {},
"timestamp": "2025-01-10T14:30:00Z"
}
Token Refresh
Use the refresh token to obtain a new access token without re-entering credentials. The access token must still be valid at the time of the refresh request — an expired access token will be rejected with 401 Unauthorized.
To avoid disruption, refresh the access token before it expires. The recommended approach is to refresh when less than 5 minutes remain on the current token.
Endpoint
POST /api/v1/auth/refresh
Request
Include the refresh token in the request body:
curl -X POST https://tspay-api.sandbox.travelsoftpay.com/api/v1/auth/refresh \
-H "Content-Type: application/json" \
-H "Authorization: Bearer <accessToken>" \
-d '{
"refreshToken": "eyJjdHkiOi..."
}'
Success Response (200 OK)
{
"accessToken": "eyJraWQiOi...",
"accessTokenExpiresAt": 1736519400
}
Token Lifecycle
| Token | Validity | Action when expired |
|---|---|---|
accessToken | 1 hour | Call POST /api/v1/auth/login to obtain a new session |
refreshToken | 24 hours | Call POST /api/v1/auth/login again |
Refresh the access token proactively a few minutes before it expires to avoid disruption to ongoing operations.
Session Termination
TSPay provides an endpoint to explicitly terminate an active session and invalidate the refresh token. This should be used when a credential is suspected to be compromised or when a session must be closed programmatically.
Endpoint
POST /api/v1/auth/logout
Request
Include the refresh token to revoke in the request body, and the current access token in the Authorization header:
curl -X POST https://tspay-api.sandbox.travelsoftpay.com/api/v1/auth/logout \
-H "Content-Type: application/json" \
-H "Authorization: Bearer <accessToken>" \
-d '{
"refreshToken": "eyJjdHkiOi..."
}'
Success Response (200 OK)
{
"message": "Session terminated successfully"
}
Behaviour
| Token | Effect after logout |
|---|---|
refreshToken | Immediately and permanently invalidated. Any subsequent call to /api/v1/auth/refresh using this token returns 401. |
accessToken | Remains technically valid until its natural 1-hour expiry. This is a known characteristic of stateless JWT tokens. |
After calling
/api/v1/auth/logout, the refresh token is immediately invalidated. The access token in circulation will continue to be accepted by the API until it expires naturally (within 1 hour of issuance). For time-sensitive compromise scenarios, contact TSPay support to deactivate the credential entirely — this blocks all further token issuance and API access regardless of active token state.
Error Response
{
"correlationId": "2aaa9f82-4873-4ba9-a0a3-e2228ff25078",
"status": 401,
"message": "Authentication failed",
"details": {},
"timestamp": "2025-01-10T14:30:00Z"
}
When to Use
| Scenario | Recommended action |
|---|---|
| Planned session end | Call POST /api/v1/auth/logout |
| Suspected credential compromise | Call POST /api/v1/auth/logout and contact TSPay support to deactivate the credential |
| Refresh token expired naturally | No action needed — call POST /api/v1/auth/login to obtain a new session |
JWT Token Structure
Access tokens are JWTs issued by TSPay. For most integrations, treat the token as an opaque bearer token and rely on the expiry fields returned by the API rather than parsing the token client-side.
Decoded Token Payload
{
"sub": "acme_corp",
"iat": 1736512200,
"exp": 1736515800
}
| Claim | Description |
|---|---|
sub | Subject — identifier of the authenticated credential |
iat | Issued at timestamp |
exp | Expiration timestamp |
Token Expiration: Access tokens expire after 1 hour. Use
POST /api/v1/auth/refreshto obtain a new access token without logging in again.
Refresh Token Rotation: Each successful call to
POST /api/v1/auth/refreshissues a new refresh token. The previous refresh token remains valid for a short grace period (up to 60 seconds) to accommodate concurrent requests, after which it is permanently invalidated. Always store and use the most recently issued refresh token. If a401is received on/api/v1/auth/refresh, callPOST /api/v1/auth/loginto obtain a new session.
Using JWT Tokens
Authorization Header
All authenticated requests must include the Authorization header with the Bearer prefix, a space, and then the access token:
Authorization: Bearer YOUR_ACCESS_TOKEN
The
Bearerprefix (including the trailing space) is required. Omitting it or using a different scheme will result in a401 Unauthorizedresponse.
Example Authenticated Request
curl -X POST https://tspay-api.sandbox.travelsoftpay.com/api/v1/issuing/cards \
-H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..." \
-H "Content-Type: application/json" \
-d '{...card_data...}'
Authentication vs Authorization
TSPay applies two distinct access control layers on every request:
| Code | Meaning | Typical cause |
|---|---|---|
401 Unauthorized | Authentication failure — the request could not be authenticated | Missing, invalid, or expired token; wrong credentials on login |
403 Forbidden | Authorization failure — authenticated but not permitted for this operation | Credential lacks the required permission |
Always handle both codes in your integration. A 403 response means authentication succeeded but the operation requires elevated permissions — contact TSPay support to review your credential’s permission set.
Token Management Best Practices
1. Secure Storage
Since TSPay is a server-to-server API, tokens must be stored and managed server-side only. Never expose tokens to client applications, end users, logs, or monitoring systems.
Never store tokens in:
- Version control systems
- Application or server logs
- URL parameters or query strings
- Monitoring or observability pipelines
Recommended storage locations:
- Encrypted environment variables
- Secure credential management systems (e.g., HashiCorp Vault, AWS Secrets Manager)
- Server-side token stores with restricted access
2. Token Refresh Strategy
Store the accessTokenExpiresAt value returned by the login or refresh endpoint and use it to determine when to refresh the token.
// Refresh when less than 5 minutes remain
if (tokenExpiresAt - Math.floor(Date.now() / 1000) < 300) {
await refreshAccessToken();
}
3. Handle Authentication Errors
| Scenario | Action |
|---|---|
401 on an API call (token expired) | The access token has expired. Call POST /api/v1/auth/login to obtain a new session. To avoid this, refresh the access token before it expires using POST /api/v1/auth/refresh. |
401 on /api/v1/auth/refresh | Either the access token has expired, the refresh token has expired, or credentials are invalid — call POST /api/v1/auth/login to obtain a new session. |
401 on /api/v1/auth/logout | Refresh token is already expired or invalid — session is effectively already terminated; no further action needed. |
403 on an API call | Credential lacks permission — do not retry; contact TSPay support. |
Security Considerations
Transport Security
All API requests MUST use HTTPS. HTTP requests will be rejected.
Support Tracing
Every error response includes a correlationId field. When contacting TSPay support, always provide this value — it allows the team to locate the exact request in logs.
{
"correlationId": "2aaa9f82-4873-4ba9-a0a3-e2228ff25078",
"status": 401,
"message": "Authentication failed",
"details": {},
"timestamp": "2025-01-10T14:30:00Z"
}
Rate Limiting
Authentication endpoints are rate-limited per API credential to prevent brute-force attacks:
| Endpoint | Limit |
|---|---|
/api/v1/auth/login | 5 requests/minute |
/api/v1/auth/refresh | 5 requests/minute |
Exceeding this limit returns 429 Too Many Requests. Wait for the duration specified in the retryAfter field before retrying:
{
"correlationId": "...",
"status": 429,
"message": "Rate limit exceeded",
"retryAfter": 60
}
Credential Rotation
- Use separate credentials for Sandbox and Production
- Rotate credentials regularly according to your organisation’s security policies. TSPay may enforce credential rotation policies in future versions of the platform
- Rotate immediately if credentials are suspected to be compromised
Audit Logging
TSPay maintains audit logs for all API activity, including authentication events, card creation requests, and access to plaintext card data. Audit logs are retained in accordance with TSPay’s internal compliance policy. Provide the correlationId from any error response when contacting TSPay support — this value references the specific request in TSPay’s audit trail.
Common Authentication Errors
Invalid Credentials (401)
Cause: Wrong username or password
Solution:
- Verify your credentials with TSPay support
- Check for trailing spaces or encoding issues in the password value
Access Token Expired (401)
Cause: JWT access token has passed its 1-hour expiration
Solution:
- Call
POST /api/v1/auth/loginto obtain a new session. The refresh endpoint requires a valid (non-expired) access token and cannot be used after expiration.
Missing or Malformed Authorization Header (401)
Cause: Request is missing the Authorization header, or it does not follow the Bearer <token> format
Solution:
- Add
Authorization: Bearer <access_token>to all authenticated requests - Verify the token is complete and unmodified
Insufficient Permissions (403)
Cause: The credential is valid but lacks the permission required for this operation
Solution:
- Review which permission the operation requires (documented per endpoint)
- Contact TSPay support to update your credential’s permission set
Next Steps
Now that you understand authentication:
- Virtual Cards - Create virtual cards
- Getting Started - Complete integration guide