Files
adif-api-reverse-engineering/docs/API_DOCUMENTATION.md

8.5 KiB

ADIF Elcano API - Complete Documentation

Complete documentation of the ADIF API (El Cano Movil) obtained through reverse engineering of the mobile application.

All 9/9 endpoints functional | HMAC-SHA256 authentication | 1587 station codes


Base URLs

BASE_URL_STATIONS     = https://estaciones.api.adif.es
BASE_URL_CIRCULATION  = https://circulacion.api.adif.es
BASE_URL_ELCANOWEB    = https://elcanoweb.adif.es/api/
BASE_URL_AVISA        = https://avisa.adif.es/avisa-ws/api/

Authentication Keys

HMAC Keys (extracted from libapi-keys.so)

ACCESS_KEY = "and20210615"
SECRET_KEY = "Jthjtr946RTt"

User-keys (different for each service)

Circulations: f4ce9fbfa9d721e39b8984805901b5df
Stations:     0d021447a2fd2ac64553674d5a0c1a6f

Other Auth Tokens

Avisa Login:        Basic YXZpc3RhX2NsaWVudF9hbmRyb2lkOjh5WzZKNyFmSjwhXypmYXE1NyNnOSohNElwa2MjWC1BTg==
Subscriptions:      Basic ZGVpbW9zOmRlaW1vc3R0
Registration Token: Bearer b9034774-c6e4-4663-a1a8-74bf7102651b

Endpoints (9/9 Working)

Station Endpoints

Get All Stations

GET /portroyalmanager/secure/stations/allstations/reducedinfo/{token}/
Base: https://estaciones.api.adif.es
User-key: stations
Token: Initial value is "0"

Get Station Details

POST /portroyalmanager/secure/stations/onestation/
Base: https://estaciones.api.adif.es
User-key: stations

Body:
{
  "stationCode": "10200",
  "token": "0",
  "detailedInfo": {
    "extendedStationInfo": true,
    "stationActivities": true,
    "stationBanner": true,
    "stationCommercialServices": true,
    "stationInfo": true,
    "stationServices": true,
    "stationTransportServices": true
  }
}

Station Observations

POST /portroyalmanager/secure/stationsobservations/
Base: https://estaciones.api.adif.es
User-key: stations

Body:
{
  "stationCodes": ["60000", "71801"]
}

Circulation Endpoints

Departures

POST /portroyalmanager/secure/circulationpaths/departures/traffictype/
Base: https://circulacion.api.adif.es
User-key: circulations

Body:
{
  "commercialService": "BOTH",
  "commercialStopType": "BOTH",
  "page": {"pageNumber": 0},
  "stationCode": "10200",
  "trafficType": "ALL"
}

Arrivals

POST /portroyalmanager/secure/circulationpaths/arrivals/traffictype/
Base: https://circulacion.api.adif.es
User-key: circulations

Body: Same format as departures

Between Stations

POST /portroyalmanager/secure/circulationpaths/betweenstations/traffictype/
Base: https://circulacion.api.adif.es
User-key: circulations

Body:
{
  "commercialService": "BOTH",
  "commercialStopType": "BOTH",
  "originStationCode": "10200",
  "destinationStationCode": "71801",
  "page": {"pageNumber": 0},
  "trafficType": "ALL"
}

IMPORTANT: Send only origin/destination; do NOT add `stationCode` or explicit `null` fields.

One Train Route (OnePaths)

POST /portroyalmanager/secure/circulationpathdetails/onepaths/
Base: https://circulacion.api.adif.es
User-key: circulations

Body:
{
  "allControlPoints": true,
  "commercialNumber": "04138",
  "destinationStationCode": "60000",
  "launchingDate": 1733356800000,
  "originStationCode": "71801"
}

Notes:
- `commercialNumber` must be real (grab from departures/arrivals)
- `launchingDate` must be milliseconds
- Omit unused fields instead of sending `null`

Multiple Routes (SeveralPaths)

POST /portroyalmanager/secure/circulationpathdetails/severalpaths/
Base: https://circulacion.api.adif.es
User-key: circulations

Body: Same format as onepaths (omit unused fields)

Returns 204 when there is no data for the requested trains.

Train Composition

POST /portroyalmanager/secure/circulationpaths/compositions/path/
Base: https://circulacion.api.adif.es
User-key: circulations

Body:
{
  "commercialNumber": "03194",
  "destinationStationCode": "71801",
  "launchingDate": 1764889200000,
  "originStationCode": "10200"
}

IMPORTANT: Do NOT include `allControlPoints: true` (requires elevated permissions, returns 401)

Response Structures

Departures/Arrivals Response

{
  "commercialPaths": [
    {
      "commercialPathInfo": {
        "timestamp": 1764927847100,
        "commercialPathKey": {
          "commercialCirculationKey": {
            "commercialNumber": "90399",
            "launchingDate": 1764889200000
          },
          "originStationCode": "10620",
          "destinationStationCode": "60004"
        },
        "trafficType": "CERCANIAS",
        "opeProComPro": {
          "operator": "RF",
          "product": "C",
          "commercialProduct": " "
        }
      },
      "passthroughStep": {
        "stopType": "NO_STOP",
        "stationCode": "10200",
        "departurePassthroughStepSides": {
          "plannedTime": 1764927902000,
          "forecastedOrAuditedDelay": 2175,
          "timeType": "FORECASTED",
          "plannedPlatform": "2",
          "circulationState": "RUNNING"
        }
      }
    }
  ]
}

Important fields:

  • commercialNumber: Train commercial number
  • launchingDate: Date in milliseconds
  • plannedTime: Planned time in milliseconds
  • forecastedOrAuditedDelay: Delay in seconds
  • circulationState: RUNNING, PENDING_TO_CIRCULATE, etc.

OnePaths Response (Complete Route)

{
  "commercialPaths": [
    {
      "commercialPathInfo": { /* Same as departures */ },
      "passthroughSteps": [  // Array with ALL stops
        {
          "stopType": "COMMERCIAL",
          "stationCode": "10620",
          "departurePassthroughStepSides": {
            "plannedTime": 1764918000000,
            "forecastedOrAuditedDelay": 430,
            "plannedPlatform": "1",
            "circulationState": "RUNNING"
          }
        }
        // ... more stops
      ]
    }
  ]
}

Key difference:

  • departures/arrivals -> passthroughStep (singular)
  • onepaths -> passthroughSteps (plural, array)

Status Codes

Code Meaning Cause
200 Success Request successful with data
204 No Content Authentication OK, no data available
400 Bad Request Incorrect payload
401 Unauthorized No permissions or wrong auth

Note: 204 is NOT an error. It means authentication and payload are correct.


Data Types

TrafficType

  • CERCANIAS - Commuter trains
  • AVLDMD - High Speed, Long and Medium Distance
  • OTHERS - Other
  • TRAVELERS - Passengers
  • GOODS - Freight
  • ALL - All types

State (commercialService, commercialStopType)

  • YES
  • NOT
  • BOTH

Common Issues and Solutions

Issue 1: NULL Fields Cause Errors

Problem: Including null fields in JSON causes 401 errors.

// WRONG - causes 401
{"stationCode": null, "trafficType": "ALL"}

// CORRECT - omit the field entirely
{"trafficType": "ALL"}

Issue 2: Header Order for HMAC

The canonical headers order is NOT alphabetical:

1. content-type
2. x-elcano-host    <- host before client!
3. x-elcano-client
4. x-elcano-date
5. x-elcano-userid

Issue 3: Timestamps

launchingDate must be in milliseconds:

# Correct
timestamp = int(datetime.now().timestamp() * 1000)

# Wrong (missing 3 zeros)
timestamp = int(datetime.now().timestamp())

Issue 4: allControlPoints Requires Permissions

/compositions/path/ with allControlPoints: true returns 401. Don't include it.


Station Codes

Total: 1587 stations File: station_codes.txt Source: apk_extracted/assets/stations_all.json

Top Stations

10200    Madrid Puerta de Atocha          AVLDMD
10302    Madrid Chamartin-Clara Campoamor AVLDMD
71801    Barcelona Sants                  AVLDMD,CERCANIAS
60000    Valencia Nord                    AVLDMD
11401    Sevilla Santa Justa              AVLDMD
50003    Alicante Terminal                AVLDMD,CERCANIAS
54007    Cordoba Central                  AVLDMD
79600    Zaragoza Portillo                AVLDMD,CERCANIAS

Implementation Notes

Tools Used

  • Ghidra: Key extraction from libapi-keys.so
  • JADX: APK decompilation
  • Python 3: Client implementation

Key Source Files

  • apk_extracted/lib/x86_64/libapi-keys.so - Authentication keys
  • apk_extracted/assets/stations_all.json - Station database
  • apk_decompiled/sources/com/adif/elcanomovil/serviceNetworking/interceptors/auth/ElcanoAuth.java - HMAC algorithm

Main Analyzed Classes

  • TrafficCirculationPathRequest - Request model
  • OneOrSeveralPathsRequest - Path requests
  • ElcanoAuth - HMAC-SHA256 implementation
  • ServicePaths - URL and key definitions