Retrieve & Delete Travel Data

Read stored trips, activities, and calculations back — and delete a trip — with the /v2/travel-data/trips endpoints.

After ingestion, the trip endpoints let you read back exactly what SQUAKE stored, and delete a trip you shouldn't have sent.

Use them to reconcile your system against SQUAKE, to fetch the calculations of additional methodologies, to look up the audit log of a stored calculation, or to clean up test and duplicate data.

Endpoints

EndpointReturns
GET /v2/travel-data/tripsPaginated list of your stored trips, newest ingested first.
GET /v2/travel-data/trips/{id}A single trip.
GET /v2/travel-data/trips/{trip_id}/activitiesAll activities of a trip, each with its traveler and calculations.
GET /v2/travel-data/trips/{trip_id}/activities/{activity_id}A single activity with its traveler and calculations.
DELETE /v2/travel-data/trips/{id}Deletes the trip, its activities, and their calculations.

Authentication

  • Bearer Token or HTTP Basic Auth (API key as username, no password) — the same credentials you use for POST /v2/travel-data.

Environments

EnvironmentBase URL
Sandboxhttps://api.sandbox.squake.earth
Productionhttps://api.squake.earth

Ingestion must be enabled

Ingesting travel data must be enabled for your account — until it is, POST /v2/travel-data and the CSV imports respond with 404 and error code travel_data_not_found. The read endpoints serve whatever is stored for you; a client with nothing stored gets an empty list.

The two-level shape

A trip response carries the trip's own attributes and the IDs of its activities, never the activities themselves. The activities — with their traveler, calculations, and audit references — live one request away:

GET /v2/travel-data/trips/{id}
  ├── trip_reference / client_id / submitted_by_client_id
  ├── trip_name / trip_purpose / trip_status
  ├── start_date_utc / end_date_utc
  ├── booking_provider / amount_in_cents / currency
  ├── cost_center { name, purpose, email }
  └── activity_ids[]  ────────────┐
                                  │
GET /v2/travel-data/trips/{id}/activities
  └── activities[]  ←─────────────┘
        ├── type / external_reference
        ├── origin / destination / fuel_type   (by type — see below)
        ├── traveler { name, email, traveler_type }
        └── calculations[]
              ├── methodology / methodology_version
              ├── co2_in_grams
              └── audit { id }

This keeps a trip response small and predictable: no matter how many activities a trip holds, reading it costs one bounded response, and every endpoint that returns a trip returns the same set of fields.

The one exception is the trips array of a submission, which reports the trip's identity only — id, trip_reference, client_id, submitted_by_client_id, activity_ids. A read returns those five fields plus the descriptive ones above, and a shared field means the same thing in both, so a submission response and a later read of the same trip never disagree.

Addressing a trip by your own reference

Everywhere a trip is addressed, you can pass either SQUAKE's id (a UUID) or your own trip_reference:

# equivalent, assuming trip_reference "TRIP-EXAMPLE-002" belongs to this trip
GET /v2/travel-data/trips/7c9e6f2a-4b81-4d3e-9a52-1f0c8b3d5e47
GET /v2/travel-data/trips/TRIP-EXAMPLE-002

That means you do not have to store SQUAKE's IDs to read your data back — if you set trip_reference on ingestion, your own reference is enough. Activities, by contrast, are always addressed by their SQUAKE id, as listed in the trip's activity_ids.

Getting the IDs from a submission

POST /v2/travel-data returns a trips array next to the flat items, with the ID of every trip the request persisted and the IDs of the activities written under it:

200 OKapplication/json
{
  "carbon_quantity": 333.0,
  "carbon_unit": "kilogram",
  "items": [ ... ],
  "trips": [
    {
      "id": "7c9e6f2a-4b81-4d3e-9a52-1f0c8b3d5e47",
      "trip_reference": "TRIP-EXAMPLE-002",
      "client_id": "4fb3ec7a-92cb-471f-b75f-c3fe7c2a34d5",
      "submitted_by_client_id": "4fb3ec7a-92cb-471f-b75f-c3fe7c2a34d5",
      "activity_ids": [
        "2b4d6f81-9c3e-4a57-b0d2-6e1f8a4c7b93",
        "5e1a9c34-7d62-4f08-b3a1-9c4e2d7f6b58",
        "c84f1d27-3e95-4b60-a7d8-2f1b9e6c4a03",
        "a1d3b5c7-2e94-4f68-9b0a-7c5d3e1f8a26"
      ]
    }
  ]
}

Three details worth knowing:

  • One entry per trip, not per payload entry. Two payload entries sharing a trip_reference resolve to the same trip and are reported once.
  • activity_ids covers what this request wrote. Re-using a trip_reference appends activities to an existing trip, so that trip holds more activities than the submission response lists. Read the trip to get the full list.
  • A submission echoes identity, not content. The five fields above are all a trips entry carries — it does not repeat the trip name, dates, amount or cost center you just sent. Read the trip to get those back as SQUAKE stored them.

Trip fields

FieldTypeDescription
idstring (UUID)SQUAKE's identifier for the trip.
trip_referencestring | nullYour own reference, echoed from ingestion. Usable in place of id.
client_idstring (UUID)The client that owns the trip.
submitted_by_client_idstring (UUID) | nullThe client whose credentials submitted the trip. null for trips that did not arrive through POST /v2/travel-data (e.g. CSV imports or TMC integrations).
activity_idsarray (UUID)IDs of the trip's activities, in ingestion order, across all activity types.

client_id and submitted_by_client_id differ when you submit on behalf of a descendant client: the target client owns the trip, your credential is recorded as the submitter. See Submitting on behalf of another client.

Reads return more. On top of the five fields above, GET /v2/travel-data/trips and GET /v2/travel-data/trips/{id} return the rest of what you described on ingestion:

FieldTypeDescription
trip_namestring | nullThe trip's name, echoed from ingestion.
trip_purposestring | nullThe trip's purpose, echoed from ingestion.
trip_statusstring | nullWhere the trip stands: one of planned, confirmed, in_progress, completed, canceled, or null. Normalized on ingestion — never the source's own wording. See Trip status.
booking_providerstring | nullThe provider the trip was booked through, echoed from ingestion.
start_date_utcstring (date-time) | nullStart of the trip, in UTC. Ingestion accepts a date (2026-03-10); reads return a date-time (2026-03-10T00:00:00Z).
end_date_utcstring (date-time) | nullEnd of the trip, in UTC, same format.
amount_in_centsinteger | nullThe booking's own amount, in the minor unit of currency (10000 is 100.00 USD).
currencystring | null3-letter ISO 4217 code of amount_in_cents.
cost_centerobject | nullThe cost center the trip is booked against, as { "name", "purpose", "email" }. The key is always present; it is null for a trip booked against none.

These fields are echoed as stored, so a field you did not send on ingestion reads back as null. The one field that isn't echoed verbatim is trip_status, which is normalized — see below. Two values SQUAKE keeps internally are deliberately not here: the amount converted into your base currency for reporting, and the cost center's department and internal id.

Trip status

trip_status is the one trip field SQUAKE does not hand back as you sent it. Source systems each have their own vocabulary — TICKETED, past, Zurückliegend, storno — so the value is mapped onto a fixed set on ingestion and reads only ever return one of these:

ValueMeaning
plannedThe booking exists, but nothing is held yet.
confirmedThe booking is firm and the trip is still ahead.
in_progressThe trip is under way.
completedThe trip has finished.
canceledThe trip is called off.
nullThe trip carries neither a status nor a date, so there is nothing to report.

The dates usually decide it. A trip whose end_date_utc has passed reads completed, and one already under way reads in_progress — whatever status you sent. Cancellation is the exception that outranks the dates: a trip called off after its end date is canceled, not completed. Only when the dates can't settle it does your status matter, and then a trip you called firm reads confirmed and everything else reads planned.

A trip you sent no dates for keeps the reading of the status you sent, rather than collapsing to planned.

It keeps up on its own. Statuses that depend on the date are re-evaluated daily, so a trip moves through confirmed, in_progress and completed without you sending anything. Polling for that transition is fine; re-submitting the trip to force it isn't necessary.

When you submit a status, send your own wording — SQUAKE recognises the common ones (booked, ticketed, upcoming, checked_in, past, cancelled, void and others), and case, separators and accents do not matter. A wording it doesn't recognise doesn't fail the request; the trip's dates decide the status instead.

List trips

GET /v2/travel-data/trips

Returns your stored trips as a bare array, most recently ingested first. Trips from every source are listed — API submissions, CSV imports, and TMC integrations alike.

Pagination

page

Page number, starting at 1.

items

Trips per page. Defaults to 20, capped at 100.

See every field of GET /v2/travel-data/trips in the API reference

Pagination metadata is returned in the response headers:

HeaderMeaning
Current-PageThe page returned.
Page-ItemsTrips per page.
Total-PagesTotal number of pages.
Total-CountTotal number of trips.
LinkRFC 8288 links to the first, prev, next, and last page.

Example

curl --request GET "https://api.sandbox.squake.earth/v2/travel-data/trips?page=1&items=20" \
  --header "Authorization: Bearer $ACCESS_TOKEN"
200 OKapplication/json
[
  {
    "id": "7c9e6f2a-4b81-4d3e-9a52-1f0c8b3d5e47",
    "trip_reference": "TRIP-EXAMPLE-002",
    "client_id": "4fb3ec7a-92cb-471f-b75f-c3fe7c2a34d5",
    "submitted_by_client_id": "4fb3ec7a-92cb-471f-b75f-c3fe7c2a34d5",
    "trip_name": "Example business trip to London",
    "trip_purpose": "Customer workshop",
    "trip_status": "completed",
    "booking_provider": "Example TMC",
    "start_date_utc": "2026-03-10T00:00:00Z",
    "end_date_utc": "2026-03-12T00:00:00Z",
    "amount_in_cents": 248500,
    "currency": "EUR",
    "cost_center": {
      "name": "Example Cost Center",
      "purpose": "Sales",
      "email": "cost-center@example.com"
    },
    "activity_ids": [
      "2b4d6f81-9c3e-4a57-b0d2-6e1f8a4c7b93",
      "5e1a9c34-7d62-4f08-b3a1-9c4e2d7f6b58",
      "c84f1d27-3e95-4b60-a7d8-2f1b9e6c4a03",
      "a1d3b5c7-2e94-4f68-9b0a-7c5d3e1f8a26"
    ]
  },
  {
    "id": "0e5c3a91-6d78-4f21-8b4e-5a2d9c1f7b60",
    "trip_reference": "TRIP-EXAMPLE-001",
    "client_id": "4fb3ec7a-92cb-471f-b75f-c3fe7c2a34d5",
    "submitted_by_client_id": null,
    "trip_name": null,
    "trip_purpose": null,
    "trip_status": null,
    "booking_provider": null,
    "start_date_utc": "2026-02-04T00:00:00Z",
    "end_date_utc": null,
    "amount_in_cents": null,
    "currency": null,
    "cost_center": null,
    "activity_ids": ["d3c7b9a5-1f48-4e02-9a6d-8b5c3f1e7d24"]
  }
]

The second trip shows what a sparsely described source looks like: every field it did not carry reads back as null, and the keys are still there.

Multi-client accounts. If submitting on behalf of other clients is enabled for you, the list spans the authenticated client and all of its descendants — never a parent, a sibling, or an unrelated client. Read client_id on each trip to see which client owns it. Without that capability, you only ever see your own trips.

Retrieve a single trip

GET /v2/travel-data/trips/{id}

curl --request GET "https://api.sandbox.squake.earth/v2/travel-data/trips/TRIP-EXAMPLE-002" \
  --header "Authorization: Bearer $ACCESS_TOKEN"
200 OKapplication/json
{
  "id": "7c9e6f2a-4b81-4d3e-9a52-1f0c8b3d5e47",
  "trip_reference": "TRIP-EXAMPLE-002",
  "client_id": "4fb3ec7a-92cb-471f-b75f-c3fe7c2a34d5",
  "submitted_by_client_id": "4fb3ec7a-92cb-471f-b75f-c3fe7c2a34d5",
  "trip_name": "Example business trip to London",
  "trip_purpose": "Customer workshop",
  "trip_status": "completed",
  "booking_provider": "Example TMC",
  "start_date_utc": "2026-03-10T00:00:00Z",
  "end_date_utc": "2026-03-12T00:00:00Z",
  "amount_in_cents": 248500,
  "currency": "EUR",
  "cost_center": {
    "name": "Example Cost Center",
    "purpose": "Sales",
    "email": "cost-center@example.com"
  },
  "activity_ids": [
    "2b4d6f81-9c3e-4a57-b0d2-6e1f8a4c7b93",
    "5e1a9c34-7d62-4f08-b3a1-9c4e2d7f6b58",
    "c84f1d27-3e95-4b60-a7d8-2f1b9e6c4a03",
    "a1d3b5c7-2e94-4f68-9b0a-7c5d3e1f8a26"
  ]
}

A single trip read and a trip in the list return the same fields — the list is the same shape, paginated.

List the activities of a trip

GET /v2/travel-data/trips/{trip_id}/activities

Returns every activity of the trip, in ingestion order, each with its traveler and the calculations persisted for it. A trip is a single booking, so the list is bounded and returned in full — this endpoint isn't paginated.

curl --request GET "https://api.sandbox.squake.earth/v2/travel-data/trips/TRIP-EXAMPLE-002/activities" \
  --header "Authorization: Bearer $ACCESS_TOKEN"
200 OKapplication/json
[
  {
    "id": "2b4d6f81-9c3e-4a57-b0d2-6e1f8a4c7b93",
    "type": "flight",
    "trip_id": "7c9e6f2a-4b81-4d3e-9a52-1f0c8b3d5e47",
    "client_id": "4fb3ec7a-92cb-471f-b75f-c3fe7c2a34d5",
    "external_reference": "ACTIVITY-FLIGHT-OUTBOUND",
    "origin": "BER",
    "destination": "LHR",
    "traveler": {
      "name": "Jane Doe",
      "email": "jane.doe@example.com",
      "traveler_type": "employee"
    },
    "calculations": [
      {
        "id": "3f2a1c8e-5d47-4b91-a0c3-1e8b7d6f4a20",
        "methodology": "TIM",
        "methodology_version": "3.0.0",
        "co2_in_grams": 142600.0,
        "audit": { "id": "0194d6b4-8711-7873-899d-c8133cd742f8" }
      }
    ]
  },
  {
    "id": "5e1a9c34-7d62-4f08-b3a1-9c4e2d7f6b58",
    "type": "hotel",
    "trip_id": "7c9e6f2a-4b81-4d3e-9a52-1f0c8b3d5e47",
    "client_id": "4fb3ec7a-92cb-471f-b75f-c3fe7c2a34d5",
    "external_reference": "ACTIVITY-HOTEL-001",
    "traveler": {
      "name": "Jane Doe",
      "email": "jane.doe@example.com",
      "traveler_type": "employee"
    },
    "calculations": [
      {
        "id": "8b6d4f20-9a17-4c53-b8e1-3d7a5c2f9e14",
        "methodology": "DEFRA",
        "methodology_version": "2026",
        "co2_in_grams": 36800.0,
        "audit": null
      }
    ]
  }
]

The example is truncated to two of the trip's four activities. Note that the hotel stay has no origin/destination keys at all — see Keys that depend on the activity type.

Activity fields

FieldTypeDescription
idstring (UUID)Identifier of the activity, as listed in the trip's activity_ids.
typestringflight, hotel, car, or train.
trip_idstring (UUID)The trip this activity belongs to.
client_idstring (UUID)The client that owns the activity — always the owner of its trip.
external_referencestring | nullYour own reference, echoed from ingestion.
originstring | nullWhere the activity started, echoed from ingestion in the format you sent it (airport code, address, UN/LOCODE, lat/long). flight, car, train only.
destinationstring | nullWhere it ended, same format as origin. flight, car, train only.
fuel_typestring | nullThe fuel the activity was calculated with, echoed from ingestion. Accepted values depend on the methodology. car and train only.
travelerobject | nullWho travelled, as { "name", "email", "traveler_type" }. Always present; null for an activity submitted without a traveler.
calculationsarrayOne entry per methodology calculated for this activity, oldest first.

Keys that depend on the activity type

Route and fuel are only stored for the activity types that have them, and a key a type cannot have is omitted rather than returned as null:

Typeorigin / destinationfuel_type
flightYes—
carYesYes
trainYesYes
hotel——

So read type first, and treat a missing key as "not applicable to this type" rather than "no value". traveler, by contrast, is present on every activity type — a null there means the activity was submitted without a traveler, which is common for CSV and TMC rows.

Traveler fields

FieldTypeDescription
namestring | nullName of the traveler, echoed from ingestion.
emailstring | nullEmail of the traveler, echoed from ingestion and lower-cased. SQUAKE matches travelers on this within a client, so two activities for the same person return the same traveler.
traveler_typestring | nullemployee, contractor, or guest.

The traveler's department, your external employee id and SQUAKE's internal traveler id are not part of this response.

Calculation fields

FieldTypeDescription
idstring (UUID)Identifier of the calculation.
methodologystringThe methodology this calculation ran under (e.g. TIM, DEFRA).
methodology_versionstring | nullPublished version of that methodology for this activity type. null for methodologies that are not versioned.
co2_in_gramsnumberEmissions in grams of CO₂ equivalent.
auditobject | nullThe audit log this calculation produced, as { "id": "..." }, or null for an activity submitted without audit mode.

Note the unit

co2_in_grams is in grams, while POST /v2/travel-data and the calculation endpoints report carbon_quantity in kilograms by default.

Reading the full audit log. audit carries only the ID — pass it to /v2/audits/{id} for the immutable record of the request, response, distances, and emission factors behind the calculation.

Routing legs are not listed. For a flight activity submitted with routing (e.g. BER-FRA-JFK), the individual legs are internal segments: the routed activity carries the itinerary total, and legs appear neither in activity_ids nor in this list. Addressing a leg ID directly returns 404.

Retrieve a single activity

GET /v2/travel-data/trips/{trip_id}/activities/{activity_id}

Activities are always addressed through their trip, so both identifiers are required.

curl --request GET "https://api.sandbox.squake.earth/v2/travel-data/trips/TRIP-EXAMPLE-002/activities/2b4d6f81-9c3e-4a57-b0d2-6e1f8a4c7b93" \
  --header "Authorization: Bearer $ACCESS_TOKEN"

The response is a single activity object, in the shape shown above — including its traveler and, where the type has them, origin, destination and fuel_type.

Additional methodologies

If your account is configured with more than one methodology for an activity type, SQUAKE persists one calculation per methodology. The flat items array of a submission only reports the primary methodology — the others are readable here:

200 OKapplication/json
{
  "id": "c84f1d27-3e95-4b60-a7d8-2f1b9e6c4a03",
  "type": "car",
  "trip_id": "7c9e6f2a-4b81-4d3e-9a52-1f0c8b3d5e47",
  "client_id": "4fb3ec7a-92cb-471f-b75f-c3fe7c2a34d5",
  "external_reference": "ACTIVITY-CAR-001",
  "origin": "London Heathrow Airport, United Kingdom",
  "destination": "Canary Wharf, London, United Kingdom",
  "fuel_type": "hev",
  "traveler": {
    "name": "Jane Doe",
    "email": "jane.doe@example.com",
    "traveler_type": "employee"
  },
  "calculations": [
    {
      "id": "6a2e8c40-7b19-4d35-9f68-1c4b7e2a5d93",
      "methodology": "DEFRA",
      "methodology_version": "2026",
      "co2_in_grams": 14200.0,
      "audit": null
    },
    {
      "id": "f5b1d873-2c60-4e97-a1d4-8b3f6c9e2074",
      "methodology": "US-EPA",
      "methodology_version": "2026",
      "co2_in_grams": 15100.0,
      "audit": null
    }
  ]
}

Delete a trip

DELETE /v2/travel-data/trips/{id}

Deletes the trip and everything hanging off it — all of its activities, routing legs included, and their calculations. Responds with 204 No Content and an empty body. Like the reads, it accepts a trip_reference in place of the id.

curl --request DELETE "https://api.sandbox.squake.earth/v2/travel-data/trips/TRIP-EXAMPLE-002" \
  --header "Authorization: Bearer $ACCESS_TOKEN"

Three things to keep in mind:

  • Audit logs are kept. They are an immutable record of what was calculated, so deleting a trip does not remove the audit logs its calculations produced.
  • Reporting lags a delete. Dashboards and reporting views refresh periodically, so a deleted trip can still appear in them until the next refresh.
  • This cannot be undone. Re-submitting the trip to POST /v2/travel-data restores the data, but recalculates its emissions and creates new IDs.

Errors

StatusMeaning
204Delete succeeded — empty body.
401Missing or invalid API key.
404travel_data_not_found — see below. Nothing is deleted.
500Internal server error.

Every miss on these endpoints collapses into the same response:

404 application/json
{
  "errors": [
    {
      "code": "travel_data_not_found",
      "detail": "The requested travel data does not exist or is not accessible."
    }
  ]
}

You get it when the trip does not exist, when it belongs to a client you cannot reach, when the identifier is neither a known trip id nor a known trip_reference, and when the activity belongs to a different trip or is a routing leg. A trip you cannot access is deliberately indistinguishable from one that does not exist.

The trip list is the exception: it never 404s on an empty result — a client with no reachable trips gets 200 and [].

Implementation notes

Set trip_reference on ingestion and you never need to store SQUAKE's IDs. Your own reference addresses the trip on every read and on delete. Activity IDs, however, only come from SQUAKE — take them from the submission response or from a trip read.

Reconcile through the trip, not the submission response. The trips array of a submission reports only the identity of what that request wrote. For the complete picture of a trip — its stored attributes, its cost center, and activities added by earlier or later submissions — read the trip.

Branch on type before reading an activity's route. origin, destination and fuel_type are absent, not null, for the types that do not have them, so a parser that assumes a fixed key set will break on the first hotel stay.

Use the activities endpoint for anything beyond the primary methodology. Additional methodologies and audit references are not in the flat items array of a submission; they are here.

Reads are eventually visible in reporting, deletes too. These endpoints read the stored records directly and are immediately consistent; dashboards and reporting views refresh on a schedule.

On this page

GET/v2/travel-data/trips

Run this request against the sandbox with your test key.

Open in API reference