Skip to content
Docs

Get started

Authentication

Sign in once for a bearer token, send it with every request, and sign in again when it expires.

The API authenticates users, not applications: your integration signs in with the email and password of a HoundSat user and gets a bearer token. Every request then acts as that user, with that user's role and organization, exactly as if they were using the dashboard.

Use an integration user

Give each integration its own user rather than a person's account, so a teammate leaving or changing their password never breaks it:

  1. In the dashboard (or with Invite a user), invite an address your team controls, such as integrations@yourcompany.com.
  2. Pick the lowest role that does the job: Staff to read devices, usage and messages; Admin to also import, activate and manage.
  3. Accept the invite, set a long random password, and keep it in your secrets manager.

Sign in

Send the email and password to POST /api/auth/sign_in. The answer holds the token and the user it belongs to.

Request
curl -X POST https://api.houndsat.com/api/auth/sign_in \
  -H "Content-Type: application/json" \
  -d '{"email": "integrations@acme.example", "password": "…"}'
Response200 OK
{
  "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9…",
  "user": {
    "id": "b5e1f3a9-7d2c-4e08-96b4-0c3a8f6d2e71",
    "email": "integrations@acme.example",
    "name": "Fleet sync",
    "role": "admin",
    "invite_status": "active",
    "organization_id": "a7d35c90-2e14-4f6b-8c03-5b9e1d4a2f67",
    "organization_level": "reseller"
  }
}

A wrong password, an unknown email, a deactivated user and a suspended organization all answer the same 401 invalid_credentials, so the answer never reveals which accounts exist.

Send the token

Put the token in the Authorization header of every request:

Request
curl https://api.houndsat.com/api/json/devices \
  -H "Authorization: Bearer $HOUNDSAT_TOKEN"

Keep the token on your servers. Anyone holding it can act as the integration user until it expires or is revoked.

Token lifetime

A token is valid for 14 days. It stops working earlier when:

  • you sign out with it (POST /api/auth/sign_out);
  • the user's password is reset, or the user is deactivated;
  • the user's organization, or its reseller, is suspended.

Cache the token and reuse it, and sign in again before it expires: every 7 days is a good rhythm.

GET /api/me with an expired token
HTTP/1.1 401 Unauthorized
content-type: application/json

{"error": "unauthenticated", "message": "Your session has ended or is invalid. Sign in again to continue."}

Check a token

GET /api/me returns the user behind a token, their organization and what they are allowed to do. It's a cheap way to check a token at start-up and to learn your organization's id.

GET /api/me200 OK
{
  "user": { "id": "b5e1f3a9-…", "role": "admin", "organization_level": "reseller", … },
  "organization": {
    "id": "a7d35c90-2e14-4f6b-8c03-5b9e1d4a2f67",
    "name": "Acme Connect",
    "level": "reseller",
    "parent_id": "…",
    "status": "active"
  },
  "permissions": {
    "devices.import": true,
    "devices.manage": true,
    "devices.assign": true,
    "organizations.create_child": true,
    "users.invite": true,
    "messages.retry": true,
    …
  },
  …
}

Roles and scope

Every user has one role in one organization:

RoleCan
OwnerEverything an Admin can, plus pay invoices and delete the organization. One per organization.
AdminManage users, devices, plans, invoices and webhook endpoints, and retry failed deliveries.
StaffRead devices, usage, messages and users. Never sees prices, costs or invoices, and can't change anything.

A user sees their own organization and everything below it: a reseller user sees all of the reseller's customers, a customer user only their own organization. Records outside that scope answer 404, as if they didn't exist. Each endpoint in the API reference lists who may call it.