Pular para o conteúdo principal

ACARS V2 API — Device Code Authentication

The V2 ACARS API introduces a device code authentication flow, replacing the traditional username/password login. This allows ACARS desktop clients to authenticate securely without handling user credentials.

Authentication Flow

sequenceDiagram
participant Client as ACARS Client
participant API as Airspace API
participant Pilot as Pilot (Browser)

Client->>API: POST /api/v2/acars/auth/request
API-->>Client: { user_code, authorization_token, expires_in, poll_interval }

Client->>Pilot: Display "Enter code at /acars/authorize"

loop Every poll_interval seconds
Client->>API: POST /api/v2/acars/auth/token
API-->>Client: 202 Pending
end

Pilot->>API: Enters code on /acars/authorize
Note over API: Code approved

Client->>API: POST /api/v2/acars/auth/token
API-->>Client: 200 { access_token, token_type }

Client->>API: GET /api/v2/acars/va (Bearer token)
API-->>Client: VA data

Step 1: Request a Device Code

POST /api/v2/acars/auth/request

No authentication required. No request body.

Response (201 Created):

{
"user_code": "482910",
"authorization_token": "a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2",
"expires_in": 300,
"poll_interval": 5
}
FieldTypeDescription
user_codestring6-digit code to display to the pilot
authorization_tokenstring64-character hex token (keep secret, used for polling)
expires_inintegerSeconds until the code expires (300 = 5 minutes)
poll_intervalintegerMinimum seconds between poll requests

Step 2: User Approves the Code

The pilot navigates to /acars/authorize in their browser (must be logged in to the VA), enters the 6-digit code, and clicks "Authorize Device".

The code can also be pre-filled via query parameter: /acars/authorize?code=482910.

Step 3: Poll for Access Token

POST /api/v2/acars/auth/token
Content-Type: application/json

{
"authorization_token": "a1b2c3d4e5f6..."
}
ParameterTypeRequiredDescription
authorization_tokenstringYesThe 64-character token from step 1

Response Codes:

200 OK — Approved

{
"access_token": "1|abc123def456...",
"token_type": "Bearer"
}

The device has been approved. Use the access_token as a Bearer token for all authenticated endpoints.

202 Accepted — Pending

{
"status": "pending"
}

The user hasn't approved the code yet. Continue polling every poll_interval seconds.

410 Gone — Expired

{
"status": "expired"
}

The code has expired or doesn't exist. Start a new device code flow from step 1.

429 Too Many Requests — Slow Down

{
"status": "slow_down"
}

You're polling too frequently. Wait at least poll_interval seconds between requests.


Authenticated Endpoints

Once you have an access_token, include it in the Authorization header:

Authorization: Bearer 1|abc123def456...

All V2 endpoints below require this header unless marked as public.

Pilot Profile & History

GET /api/v2/acars/pilot

Returns the authenticated pilot's profile information and 6-month flight history in a single call. This is the recommended endpoint for building pilot dashboards.

Response (200):

{
"profile": {
"id": 42,
"name": "John Doe",
"email": "[email protected]",
"profile_picture_url": "https://...",
"member_since": "2025-01-15T00:00:00+00:00",
"rank": { "name": "Captain", "level": 5, "color": "#FFD700", "icon": "captain.svg" },
"current_airport": { "icao": "KJFK", "iata": "JFK", "name": "John F. Kennedy Intl", "city": "New York" },
"base_airport": { "icao": "KLAX", "iata": "LAX", "name": "Los Angeles Intl", "city": "Los Angeles" },
"vatsim_id": "1234567",
"ivao_id": null,
"totals": {
"flights": 150,
"flight_hours": 320.5,
"points": 4500,
"avg_landing_rate": -185.3,
"last_flight_at": "2026-03-01T14:30:00+00:00"
}
},
"history": {
"months": ["2025-10", "2025-11", "2025-12", "2026-01", "2026-02", "2026-03"],
"flight_hours": [12.5, 8.3, 15.0, 6.2, 10.1, 3.5],
"flights": [5, 3, 7, 2, 4, 1],
"points": [500, 300, 700, 200, 400, 100],
"avg_landing_rate": [-180, -200, -150, -220, -170, -190]
}
}
FieldTypeDescription
profile.rankobject | nullPilot's current rank (null if unranked)
profile.current_airportobject | nullAirport where the pilot is currently located
profile.base_airportobject | nullPilot's assigned home base
profile.totalsobjectLifetime statistics
historyobject6-month monthly breakdown (same format as /history)

Also available at the V1 path: GET /api/acars/pilot

VA Information

GET /api/v2/acars/va

Returns the virtual airline's name, logos, primary color, and domains.

Response (200):

{
"name": "Example Airways",
"logo_url": "https://...",
"banner_url": "https://...",
"primary_color": "#1a73e8",
"domains": ["example.airspace.app"]
}

Active Booking

GET /api/v2/acars/booking

Returns the authenticated pilot's active booking with airline, aircraft, and airport details.

Response (200):

{
"id": 42,
"flight_number": "EX101",
"callsign": "EXA101",
"is_charter": false,
"is_alternating": false,
"expires_at": "2026-02-24T15:30:00+00:00",
"airline": { "name": "Example Airways", "icao": "EXA", "iata": "EX" },
"aircraft": { "registration": "N12345", "name": "Spirit of Adventure", "type": "B738", "subfleet": "738", "fleet": "Boeing 737" },
"departure_airport": { "icao": "KJFK", "iata": "JFK", "name": "John F. Kennedy Intl", "city": "New York", "latitude": 40.6398, "longitude": -73.7789, "elevation": 13 },
"arrival_airport": { "icao": "KLAX", "iata": "LAX", "name": "Los Angeles Intl", "city": "Los Angeles", "latitude": 33.9425, "longitude": -118.4081, "elevation": 128 },
"alternate_airport": null
}

Last Position

GET /api/v2/acars/position/last

Returns the latest position report for the active booking.

Response (200):

{
"sent_at": "2026-02-24T14:30:00.000+00:00",
"latitude": 40.6398,
"longitude": -73.7789,
"altitude": 35000,
"altitude_agl": 34500,
"heading": 270,
"ground_speed": 450,
"indicated_airspeed": 280,
"true_airspeed": 460,
"vertical_speed": 0,
"phase": "cruise",
"on_ground": false
}

Start Flight

POST /api/v2/acars/start

Marks the active booking as started and clears prior position reports.

Response (200):

{
"message": "Flight started",
"trackingID": 42
}

Submit Position Report

POST /api/v2/acars/position
Content-Type: application/json

Accepts a single position object or an array of position objects. Returns 202 immediately; processing is asynchronous.

Single position:

{
"timestamp": "2026-03-08T12:00:00.123Z",
"position": { "latitude": { "value": 51.4775 }, "longitude": { "value": -0.4614 } },
"..."
}

Batch (array of positions):

[
{ "timestamp": "2026-03-08T12:00:00.000Z", "position": { "latitude": { "value": 51.0 }, "longitude": { "value": -0.5 } }, "..." },
{ "timestamp": "2026-03-08T12:00:00.250Z", "position": { "latitude": { "value": 51.5 }, "longitude": { "value": -0.6 } }, "..." }
]
Timestamp precision

timestamp is stored with millisecond precision (timestamp(3) in MySQL/Postgres). All of the following formats are accepted, so existing clients that report at whole-second resolution continue to work unchanged:

  • ISO-8601 string with fractional seconds — "2026-03-08T12:00:00.123Z"
  • ISO-8601 string with whole seconds — "2026-03-08T12:00:00Z"
  • Unix epoch seconds (integer or numeric string) — 1741435200
  • Unix epoch milliseconds (13-digit integer or numeric string) — 1741435200123

The server parses the format automatically: any numeric value ≥ 1e12 is treated as milliseconds since epoch, anything below that as seconds. Response timestamps are always returned in ISO-8601 with the .vvv fractional component.

Key fields (per position):

FieldTypeRequiredDescription
latitudefloatYesCurrent latitude
longitudefloatYesCurrent longitude
altitudeintNoAltitude MSL (feet)
altitudeAglintNoAltitude AGL (feet)
headingintNoTrue heading (degrees)
groundSpeedintNoGround speed (knots)
indicatedAirspeedintNoIAS (knots)
trueAirspeedintNoTAS (knots)
verticalSpeedintNoVS (fpm)
flightPhasestringNoCurrent phase
onGroundboolNoOn ground flag
landingRatefloatNoLanding VS (fpm)
aircraftTypestringNoAircraft ICAO type code (e.g. B738)

Engine fields:

Each engine (1–4) supports the following nested fields:

FieldTypeDescription
engines[].n1floatN1 percentage
engines[].n2floatN2 percentage
engines[].throttlefloatThrottle position
engines[].mixturefloatMixture lever position
engines[].propellerfloatPropeller lever position
engines[].firingboolEngine running
engines[].existsboolEngine physically present on aircraft

Autopilot fields:

FieldTypeDescription
autopilot.masterboolAutopilot master switch
autopilot.headingfloatSelected heading
autopilot.altitudefloatSelected altitude
autopilot.vsfloatSelected vertical speed
autopilot.speedfloatSelected speed
autopilot.approachboolApproach mode active
autopilot.navboolNAV mode active

Radio & navigation fields:

FieldTypeDescription
radios.com1floatCOM1 frequency
radios.com2floatCOM2 frequency
radios.nav1floatNAV1 frequency
radios.nav2floatNAV2 frequency
radios.nav1ObsfloatNAV1 OBS setting
radios.nav2ObsfloatNAV2 OBS setting
radios.transponderintTransponder code
radios.transponderStateintTransponder state (0=off, 1=standby, 2=on, 3=test)

Lights fields:

FieldTypeDescription
lights.beaconboolBeacon light
lights.strobeboolStrobe lights
lights.landingboolLanding lights

Controls fields:

FieldTypeDescription
controls.elevatorfloatElevator deflection
controls.aileronfloatAileron deflection
controls.rudderfloatRudder deflection
controls.spoilersfloatSpoilers position

APU fields:

FieldTypeDescription
apu.switchOnboolAPU master switch
apu.rpmfloatAPU RPM percentage
apu.genSwitchboolAPU generator switch
apu.genActiveboolAPU generator active

Doors fields:

FieldTypeDescription
doors[].openfloatDoor open position (0.0 = closed, 1.0 = fully open)

Up to 5 doors are supported (doors[0] through doors[4]).

Environment & state fields:

FieldTypeDescription
wind.directionfloatWind direction (degrees true)
wind.speedfloatWind speed (knots)
sensors.pressureQNHfloatQNH pressure setting
sensors.gForcefloatCurrent G-force
sensors.stallWarningboolStall warning active
sensors.overspeedWarningboolOverspeed warning active
sensors.pausedboolSimulation paused
sensors.slewboolSlew mode active
sensors.crashedboolAircraft crashed
simulationRatefloatSimulation rate multiplier
totalWeightfloatAircraft total weight (lbs)
fuelWeightfloatFuel weight (lbs)

See the Scramble API docs at https://<your-domain>/docs/acars for the complete field schema and validation rules.

Response (202):

{
"message": "Position received"
}

or for batch:

{
"message": "3 positions received"
}

Cancel Flight

POST /api/v2/acars/stop

Stops the active flight and clears all position reports.

Response (200):

{
"message": "Flight canceled"
}

Finish Flight

POST /api/v2/acars/finish

Completes the flight, creates a PIREP, and deletes the booking.

Response (200):

{
"message": "Flight finished",
"pirepID": 123
}

Speech Instructions

GET /api/v2/acars/sound

Returns pending TTS speech instructions for the active booking.

Response (200):

{
"instructions": [
{ "type": "play", "url": "/api/v2/acars/sound/fragment/42" },
{ "type": "pause", "duration_ms": 500 },
{ "type": "play", "url": "/api/v2/acars/sound/combined/abc123.mp3" }
]
}

Audio Streaming (Public)

These endpoints do not require authentication:

GET /api/v2/acars/sound/fragment/{id}
GET /api/v2/acars/sound/combined/{filename}
GET /api/v2/acars/sound/upload/{filename}

Returns audio/mpeg streams.

Send Message

POST /api/v2/acars/message
Content-Type: application/json

{
"message": "Requesting pushback clearance."
}

Response (201):

{
"id": 1,
"user_id": 5,
"type": "chat",
"sender_id": 5,
"sender_name": "John Doe",
"sender_role": "user",
"message": "Requesting pushback clearance.",
"sent_at": "2026-02-24T14:30:00.000000Z",
"received_at": "2026-02-24T14:30:00.000000Z",
"read_at": null,
"created_at": "2026-02-24T14:30:00.000000Z",
"updated_at": "2026-02-24T14:30:00.000000Z"
}

Confirm Message

PUT /api/v2/acars/message/confirm
Content-Type: application/json

{
"message_id": 1
}

Marks a message as read. Returns the updated message object.

List Messages

GET /api/v2/acars/messages?page=1

Returns paginated messages (50 per page), newest first.


Interactive API Documentation

Full interactive API documentation with request/response schemas is available at:

https://<your-domain>/docs/acars

This is auto-generated by Scramble and includes both V1 and V2 endpoints.


Pilot History (Legacy)

Returns 6 months of the authenticated pilot's flight statistics — hours, flights, points, and average landing rate. Prefer /pilot which includes profile data alongside history.

GET /api/v2/acars/history
Authorization: Bearer <token>

Response:

{
"months": ["2025-10", "2025-11", "2025-12", "2026-01", "2026-02", "2026-03"],
"flight_hours": [12.5, 8.3, 15.0, 6.2, 10.1, 3.5],
"flights": [5, 3, 7, 2, 4, 1],
"points": [500, 300, 700, 200, 400, 100],
"avg_landing_rate": [-180, -200, -150, -220, -170, -190]
}
FieldTypeDescription
monthsstring[]6 month labels in YYYY-MM format (oldest first)
flight_hoursfloat[]Total accepted flight hours per month
flightsint[]Total accepted flight count per month
pointsint[]Total points earned per month
avg_landing_ratefloat[]Average landing rate (fpm) per month

Also available at the V1 path: GET /api/acars/history


VA Information — favicon_url

The VA information endpoint now includes a favicon_url field:

GET /api/v2/acars/va

The favicon_url field returns the tenant's configured favicon as a fully resolved URL, or null if not set.


Error Handling

All endpoints return JSON error responses:

{
"message": "No active booking"
}

Common error codes:

CodeMeaning
401Missing or invalid Bearer token
404Resource not found (no active booking, etc.)
410Device code expired
422Validation error
429Rate limited (polling too fast)