- Get started
- 1. Authenticate
Quickstart: authenticate
Every Legion API call carries a Bearer token issued by Keycloak, Legion’s identity provider. This page gets you a token, shows you what it contains, and proves it works with a real call. At the end you have everything the rest of the quickstarts build on.
Before you start, you need:
- An Orion account that belongs to at least one organization. If you can sign in at us-gov.orionc2.com, you are ready.
curlor Python 3.10+.
The environments used below:
| Service | URL |
|---|---|
| Legion API | https://api.usg.legion.picogrid.com |
| Keycloak (identity) | https://auth.hopper.west.prod.govcloud.legion.picogrid.com |
-
Request a token.
Keycloak issues tokens from its
tokenendpoint. You authenticate with the same username and password you use for Orion, through the publicfrontend...orionclient. The request body is form-encoded, not JSON.Terminal window curl -s "https://auth.hopper.west.prod.govcloud.legion.picogrid.com/realms/legion/protocol/openid-connect/token" \-H "Content-Type: application/x-www-form-urlencoded" \-d "grant_type=password" \-d "client_id=frontend...orion" \-d "username=you@example.com" \--data-urlencode "password=YOUR_PASSWORD"import requestsAUTH = "https://auth.hopper.west.prod.govcloud.legion.picogrid.com"resp = requests.post(f"{AUTH}/realms/legion/protocol/openid-connect/token",data={ # form-encoded, not json="grant_type": "password","client_id": "frontend...orion","username": "you@example.com","password": "YOUR_PASSWORD",},timeout=30,)resp.raise_for_status()token = resp.json()["access_token"]A successful response looks like this:
{"access_token": "eyJhbGciOiJSUzI1NiIsInR5cCIgOiAiSldUIiwia2lkIiA6...","expires_in": 1800,"refresh_expires_in": 1800,"token_type": "Bearer","scope": "email profile"}access_tokenis what you will send on every API call.expires_inis seconds: tokens live 30 minutes, after which requests return401and you request a new one the same way. -
Send it on a request.
Pass the token in the
Authorizationheader. The call below lists the organizations your account belongs to, which is also the first thing you need for every other quickstart.Terminal window curl -s "https://api.usg.legion.picogrid.com/v3/me/orgs" \-H "Authorization: Bearer $TOKEN"API = "https://api.usg.legion.picogrid.com"resp = requests.get(f"{API}/v3/me/orgs",headers={"Authorization": f"Bearer {token}"},timeout=30,)resp.raise_for_status() # fail loudly on 401/403 instead of parsing an error bodyorgs = resp.json()print(orgs){"results": [{"organization_id": "2b1c9f04-8f6e-4c1a-9a67-1d2f3e4a5b6c","organization_name": "Example Org","organization_role": "ADMIN"}]}If you see your organization listed, authentication works end to end.
-
Save the organization id.
Almost every Legion endpoint is organization-scoped and requires the
X-ORG-IDheader alongside the token. Calling one without it returns:{ "status": "error", "code": 400,"message": "Organization ID is required in either X-ORG-ID header or org-id query parameter" }Copy the
organization_idfrom step 2. The next quickstart, Create your first entity, uses it on every call.
Troubleshooting
| Response | Cause | Fix |
|---|---|---|
401 invalid_grant | Wrong username or password | Credentials are the same ones Orion accepts; the username is your full email address. |
400 unauthorized_client | Password grant disabled for this environment | Use the browser-token fallback above. |
401 after it worked earlier | Token expired (30 min) | Request a new token; nothing else changes. |
400 Organization ID is required | Org-scoped endpoint called without X-ORG-ID | Add the header with the id from step 3. |
Next steps
- Create your first entity — register a device in your organization.
- Attach a location and video stream — put it on the map and give it a feed.
- API reference — every endpoint, generated from the live spec.
Version 3.14.0 · commit 0771262
