Contratto tecnico provider Cercaviaggio

Endpoint / documentazione integrazione

Documentazione minima per integrare un provider via API (modello reference implementation). Versione contratto: v1.

Panoramica contratto

Ultimo aggiornamento: 05/06/2026. Set minimo per vendere su Cercaviaggio: catalogo (sync_*), ricerca (search), conferma live (quote), riserva (reserve) e finalizzazione (book).

Contratto stabile v1
Routing endpoint: il provider espone un’unica base URL e seleziona l’endpoint con rquest. Esempio: https://provider.example.com/rest/cercaviaggio/api2.php?rquest=quote.
Consiglio: implementa prima health, poi sync_*, poi search/quote, infine reserve/book.

Formato risposta standard

Tutti gli endpoint devono rispondere in JSON con envelope uniforme. In caso di errore success vale false e il dettaglio va in error.

{
    "success": true,
    "contract_version": "v1",
    "provider": "provider_code",
    "request_id": "cv_65f6f3e0f1d2e4.12345678",
    "data": {
        "status": "ok"
    },
    "error": null
}

Endpoint minimi

Endpoint Metodo Parametri Uso
health GET nessuno Ping + versione contratto.
sync_stops GET page, page_size, updated_since(opz.) Catalogo fermate.
sync_lines GET page, page_size, updated_since(opz.) Catalogo linee.
sync_trips GET page, page_size, updated_since(opz.) Catalogo corse + fermate ordinate.
sync_fares GET page, page_size, updated_since(opz.) Catalogo tariffe base fermata-fermata.
search GET part, arr, ad, bam, dt1 Soluzioni disponibili.
quote GET o POST part, arr, id_corsa, ad, bam, dt1, fare_id(opz.) Conferma live + quote_token.
reserve POST header X-Idempotency-Key, body quote_token Riserva posti (prima del pagamento).
cancel POST body shop_id Annulla la riserva (rollback multi-leg).
book POST header X-Idempotency-Key, body quote_token (+ shop_id opz.) Finalizza ed emette i biglietti.
change_check GET o POST ticket_code / codice / codice_camb Verifica se un biglietto può essere cambiato e ritorna i riferimenti.

Esempi minimi

Esempio sync_stops

{
    "items": [
        {
            "stop_id": 118,
            "name": "SALERNO - P.za Montpellier",
            "lat": 40.6773,
            "lon": 14.7676,
            "is_active": true,
            "updated_at": null
        }
    ],
    "page": 1,
    "page_size": 500,
    "has_more": false,
    "next_page": null,
    "synced_at": "2026-05-19T10:00:00Z"
}

Esempio sync_trips

{
    "items": [
        {
            "trip_id": 4123,
            "line_id": 3,
            "name": "SALERNO → ROMA",
            "tempo_acquisto": 30,
            "is_active": true,
            "is_visible": true,
            "stops": [
                {
                    "stop_id": 118,
                    "sequence": 1,
                    "time": "04:35",
                    "day_offset": 0,
                    "is_active": true
                },
                {
                    "stop_id": 149,
                    "sequence": 5,
                    "time": "07:40",
                    "day_offset": 0,
                    "is_active": true
                }
            ],
            "updated_at": null
        }
    ],
    "page": 1,
    "page_size": 500,
    "has_more": false,
    "next_page": null,
    "synced_at": "2026-05-19T10:00:00Z"
}

Esempio search (risposta)

{
    "solutions": [
        {
            "solution_id": "abc123",
            "departure_datetime": "2026-06-10T04:35:00+02:00",
            "arrival_datetime": "2026-06-10T07:40:00+02:00",
            "duration_minutes": 185,
            "segments": [
                {
                    "provider": "provider_code",
                    "corsa_id": 4123,
                    "from_id": 118,
                    "to_id": 149,
                    "departure_time": "04:35",
                    "arrival_time": "07:40"
                }
            ],
            "fares": [
                {
                    "fare_id": "STD",
                    "label": "Standard",
                    "amount": 24.9,
                    "currency": "EUR",
                    "seats_available": 12
                }
            ]
        }
    ]
}

Esempio quote

{
    "part": 118,
    "arr": 149,
    "id_corsa": 4123,
    "ad": 1,
    "bam": 0,
    "dt1": "21/03/2026",
    "fare_id": "STD"
}
{
    "quote_id": "6fd2f9a2a1b7cc11ef3d6a88",
    "quote_token": "eyJ2IjoxLCJwcm92aWRlciI6InByb3ZpZGVyX2NvZGUifQ.signed",
    "status": "confirmed",
    "issued_at": "2026-03-17T10:45:00+00:00",
    "expires_at": "2026-03-17T10:55:00+00:00",
    "ttl_seconds": 600,
    "trip": {
        "provider": "provider_code",
        "corsa_id": 4123,
        "direction": "outbound",
        "from_id": 118,
        "from_name": "SALERNO - P.za Montpellier - P.co Pinocchio",
        "to_id": 149,
        "to_name": "ROMA - Stazione Tiburtina",
        "departure_datetime": "2026-03-21T04:35:00+01:00",
        "arrival_datetime": "2026-03-21T07:40:00+01:00",
        "duration_minutes": 185
    },
    "passengers": {
        "ad": 1,
        "bam": 0,
        "total": 1
    },
    "pricing": {
        "fare_id": "STD",
        "label": "Standard",
        "amount": 24.9,
        "seats_available": 12,
        "currency": "EUR"
    }
}

Esempio reserve (richiesta)

{
    "quote_token": "eyJ2IjoxLCJwcm92aWRlciI6InByb3ZpZGVyX2NvZGUifQ.signed",
    "contact": {
        "full_name": "Mario Rossi",
        "email": "mario.rossi@email.it",
        "phone": "+39 333 123 4567"
    },
    "passengers": [
        {
            "full_name": "Mario Rossi",
            "birth_date": "1988-04-12"
        }
    ],
    "codice": "CV-20260519-ABCD",
    "codice_camb": ""
}

Esempio reserve (risposta)

{
    "reservation_id": "res_123",
    "reservation_token": "res_token_signed",
    "status": "reserved",
    "expires_at": "2026-05-19T10:05:00Z",
    "shop_id": "SHOP-XYZ",
    "pending_total": 24.9,
    "pending_tickets": 1,
    "tickets": [
        {
            "code": "ABCDEF123456",
            "change_code": "0",
            "passenger_name": "Mario Rossi",
            "bus": 1,
            "seat": 12,
            "price": 24.9,
            "departure_at": "2026-06-10 04:35:00",
            "arrival_at": "2026-06-10 07:40:00",
            "pdf_url": "https://provider.example.com/pdf.php?c=ABCDEF123456",
            "is_paid": false
        }
    ]
}

Esempio book (richiesta)

{
    "quote_token": "eyJ2IjoxLCJwcm92aWRlciI6InByb3ZpZGVyX2NvZGUifQ.signed",
    "shop_id": "SHOP-XYZ",
    "email": "mario.rossi@email.it",
    "nome": "Mario Rossi",
    "codice": "CV-20260519-ABCD",
    "codice_camb": ""
}

Esempio book (risposta)

{
    "booking_reference": "CVPRE-XXXXXXXXXXXXXX",
    "status": "booked",
    "ticket_issued": true,
    "shop_id": "SHOP-XYZ",
    "tickets": [
        {
            "code": "ABCDEF123456",
            "change_code": "0",
            "passenger_name": "Mario Rossi",
            "bus": 1,
            "seat": 12,
            "price": 24.9,
            "departure_at": "2026-06-10 04:35:00",
            "arrival_at": "2026-06-10 07:40:00",
            "pdf_url": "https://provider.example.com/pdf.php?c=ABCDEF123456",
            "is_paid": true
        }
    ]
}

Esempio change_check

{
    "ticket_code": "ABCDEF123456"
}
{
    "can_change": true,
    "ticket_code": "ABCDEF123456",
    "from_stop_id": 118,
    "to_stop_id": 149,
    "suggested_date_it": "10/06/2026",
    "used_changes": 0,
    "max_changes": 1,
    "changes_remaining": 1
}

Note operative (minime)

Campi minimi richiesti

Evitiamo volutamente campi non necessari (es. GTFS/transitoria). Se un campo non è richiesto qui, puoi ometterlo.