Authentication

The Sensos REST API uses JSON Web Token (JWT) bearer authentication to secure all requests. Client applications authenticate using Machine-to-Machine (M2M) application credentials provisioned at the tenant level: you exchange the credentials for an access token with a single JSON request, in the style of a client-credentials grant. This is not an OAuth 2.0 token endpoint, so generic OAuth 2.0 client libraries will not work against it.

For the full endpoint reference, see Create an access token.


Generating an Access Token

To authenticate, send an HTTP POST request containing your tenantId, applicationId, and applicationSecret to the token generation endpoint:

Endpoint

POST https://api.sensos.io/v1/applications/token
Content-Type: application/json

Request Body

{
  "tenantId": "YOUR_TENANT_ID",
  "applicationId": "YOUR_APPLICATION_ID",
  "applicationSecret": "YOUR_APPLICATION_SECRET"
}

Response

The response returns your signed JWT access token and its remaining lifetime in seconds (expiresIn). The same token is reused for up to 12 hours, so expiresIn is between about 43,200 and 86,400 seconds; a newly issued token is valid for 24 hours (86,400 seconds):

{
  "accessToken": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9...",
  "expiresIn": 86400
}

Code Examples

curl -X POST "https://api.sensos.io/v1/applications/token" \
     -H "Content-Type: application/json" \
     -d '{
           "tenantId": "YOUR_TENANT_ID",
           "applicationId": "YOUR_APPLICATION_ID",
           "applicationSecret": "YOUR_APPLICATION_SECRET"
         }'
import requests

def get_sensos_token(tenant_id: str, app_id: str, app_secret: str) -> str:
    url = "https://api.sensos.io/v1/applications/token"
    payload = {
        "tenantId": tenant_id,
        "applicationId": app_id,
        "applicationSecret": app_secret
    }
    response = requests.post(url, json=payload)
    response.raise_for_status()
    data = response.json()
    return data["accessToken"]
interface TokenResponse {
  accessToken: string;
  expiresIn: number;
}

async function getSensosToken(tenantId: string, appId: string, appSecret: string): Promise<string> {
  const response = await fetch("https://api.sensos.io/v1/applications/token", {
    method: "POST",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify({
      tenantId,
      applicationId: appId,
      applicationSecret: appSecret
    })
  });

  if (!response.ok) {
    throw new Error(`Auth failed with status: ${response.status}`);
  }

  const data = (await response.json()) as TokenResponse;
  return data.accessToken;
}
using System.Net.Http;
using System.Text;
using System.Text.Json;
using System.Threading.Tasks;

public async Task<string> GetSensosTokenAsync(string tenantId, string appId, string appSecret)
{
    using var client = new HttpClient();
    var payload = new { tenantId, applicationId = appId, applicationSecret = appSecret };
    var content = new StringContent(JsonSerializer.Serialize(payload), Encoding.UTF8, "application/json");
    
    var response = await client.PostAsync("https://api.sensos.io/v1/applications/token", content);
    response.EnsureSuccessStatusCode();
    
    var json = await response.Content.ReadAsStringAsync();
    using var doc = JsonDocument.Parse(json);
    return doc.RootElement.GetProperty("accessToken").GetString();
}

Using the Access Token

Include the token in the Authorization header of all subsequent API requests, prefixed with Bearer and a single space:

GET https://api.sensos.io/v1/accounts/{accountId}/shipments
Authorization: Bearer <YOUR_ACCESS_TOKEN>
Accept: application/json

Token Lifecycle & Caching Best Practices

  • Cache the Token: Cache the token in memory or a shared distributed cache (e.g. Redis), together with its expiry time (now + expiresIn). Do not request a new token per API call.
  • Proactive Refresh: Refresh based on expiresIn, not on a fixed 24-hour (or 23-hour) schedule: a token you receive may already be up to 12 hours old. Request a new token a few minutes before the computed expiry, and also intercept 401 Unauthorized responses to refresh the token and retry the failed call once.
  • Clock Skew: Token expiry is checked with a 5-minute clock-skew tolerance. Keep your servers' clocks synchronized (e.g. NTP).
  • Credential Storage: Never commit applicationSecret values into source control or client-side code. Use environment variables or cloud secret managers (e.g. AWS Secrets Manager, Azure Key Vault, HashiCorp Vault).

Error Handling

Token Request Errors

Errors from POST /v1/applications/token:

StatusWhenResponse body
400 Bad RequestA required field is missing, or tenantId / applicationId is not a valid GUID.RFC 7807 validation problem details (type, title, status, traceId, errors).
401 UnauthorizedThe applicationSecret is not correct.{"error": "Application secret for application <applicationId> is not correct"}
404 Not FoundThe application does not exist, or it does not belong to the given tenantId.{"error": "Application <applicationId> was not found"}
429 Too Many RequestsToo many requests (about 10 requests per second per IP address).Not JSON. No Retry-After header; retry with exponential backoff.

API Call Errors

On all other API calls, the gateway returns:

  • 401 Unauthorized with an empty body when the Authorization header is missing or malformed, or the access token is invalid or expired. Request a new token and retry once.
  • 403 Forbidden with an empty body when the token is valid but the application has no access to the account or no permission for the operation, or the tenant is inactive.

See Response Codes for the full list.


Rotating the Application Secret

If a secret may have been exposed, or as part of regular credential hygiene, rotate it:

POST https://api.sensos.io/v1/applications/{applicationId}/rotate
Authorization: Bearer <YOUR_ACCESS_TOKEN>
  • The request has no body.
  • Only the application's owners or tenant admins can rotate its secret; anyone else receives 403 Forbidden. An unknown application returns 404 Not Found.
  • The response is 200 OK with the new secret. It is shown only once, so store it immediately in your secret manager:
{
  "applicationSecret": "YOUR_NEW_APPLICATION_SECRET"
}
  • The old secret stops working immediately: token requests that use it fail with 401 Unauthorized. Update every system that requests tokens with this application before (or right after) rotating.
  • Access tokens that were already issued stay valid until they expire.

Did this page help you?