# Departure Times

Departure times represent scheduled departure times for vehicles. This API allows you to sync departure schedules from your Transportation Management System (TMS) into Flipturn, enabling features like:

1. **Late charging alerts** — notify when a vehicle may not be fully charged by its scheduled departure
2. **Smart charging optimization** — prioritize charging based on departure schedules
3. **Dashboard visibility** — show scheduled departures alongside charging status

## Sync workflow

The API is designed for a simple "sync" workflow where you POST all upcoming departures for a vehicle at once. This approach:

* **Atomically replaces** all future departures for the vehicle — if a trip was rescheduled, the outdated record is removed automatically
* **Leaves past departures unchanged** (for historical records)

A typical integration calls this endpoint periodically (e.g., every 5 minutes or hourly) to keep departure schedules up to date.

## POST /api/departure-times/vehicle

Sync all departure times for a specific vehicle. This endpoint:

1. Deletes all future departures for the specified vehicle
2. Filters out any past departures from the request (marks them as `skipped`)
3. Inserts the remaining future departures
4. Returns all departures with per-item `outcome` (`created` or `skipped`)

### Vehicle identification

You must provide **either** `vehicleId` or `vehicleVin` (not both).

* **By `vehicleVin`** *(recommended)*: If a vehicle with this VIN exists in Flipturn, the departures will be associated with it. If no matching vehicle exists, the departures are stored with the VIN for future matching — when the vehicle is later created in Flipturn with that VIN, the next sync call will automatically link the departures to the vehicle.
* **By `vehicleId`**: Use this if you know the Flipturn vehicle ID. Returns 404 if the vehicle doesn't exist.

### Request Body

```ts
{
  vehicleVin?: string;  // Either VIN or vehicle ID must be provided
  vehicleId?: number;
  departureTimes: {
    departureTime: string;   // (required) ISO 8601 datetime with optional timezone offset
    shiftName?: string;      // Name of the shift — primary display name
    routeName?: string;      // Name of the first route — secondary display name
    targetSoc?: number;      // Target state-of-charge (0–100), defaults to 90
  }[];
}
```

### Response

Each departure in the response includes an `outcome` field:

* `created` — the departure was successfully inserted
* `skipped` — the departure time is in the past and was not inserted

```ts
{
  departureTimes: {
    departureTime: string;   // ISO 8601 datetime (UTC)
    outcome: string;         // "created" | "skipped"
    id: number | null;       // Populated when created, null when skipped
    vehicleId: number | null;
    vehicleVin: string | null;
    shiftName: string | null;
    routeName: string | null;
    targetSoc: number | null;
  }[];
}
```

### Example: sync by vehicle ID

Request:

```sh
curl -X POST "https://api.getflipturn.com/api/departure-times/vehicle" \
-H "Authorization: Bearer {token}" \
-H "Content-Type: application/json" \
-d '{
  "vehicleId": 123,
  "departureTimes": [
    {
      "departureTime": "2026-02-05T06:00:00-05:00",
      "shiftName": "Morning Route",
      "routeName": "Route 42",
      "targetSoc": 90
    },
    {
      "departureTime": "2026-02-05T14:00:00-05:00",
      "shiftName": "Afternoon Route",
      "routeName": "Route 42",
      "targetSoc": 80
    }
  ]
}'
```

Response:

```js
{
  "departureTimes": [
    {
      "id": 1001,
      "vehicleId": 123,
      "vehicleVin": null,
      "departureTime": "2026-02-05T11:00:00.000Z",
      "shiftName": "Morning Route",
      "routeName": "Route 42",
      "targetSoc": 90
    },
    {
      "id": 1002,
      "vehicleId": 123,
      "vehicleVin": null,
      "departureTime": "2026-02-05T19:00:00.000Z",
      "shiftName": "Afternoon Route",
      "routeName": "Route 42",
      "targetSoc": 80
    }
  ]
}
```

### Example: sync by VIN (vehicle exists)

Request:

```sh
curl -X POST "https://api.getflipturn.com/api/departure-times/vehicle" \
-H "Authorization: Bearer {token}" \
-H "Content-Type: application/json" \
-d '{
  "vehicleVin": "1HGBH41JXMN109186",
  "departureTimes": [
    {
      "departureTime": "2026-02-05T08:00:00-05:00",
      "shiftName": "School Run AM"
    }
  ]
}'
```

Response (vehicle matched by VIN — `vehicleId` populated, `vehicleVin` null):

```js
{
  "departureTimes": [
    {
      "id": 1003,
      "vehicleId": 456,
      "vehicleVin": null,
      "departureTime": "2026-02-05T13:00:00.000Z",
      "shiftName": "School Run AM",
      "routeName": null,
      "targetSoc": null
    }
  ]
}
```

### Example: sync by VIN (vehicle does not exist yet)

If the VIN doesn't match any existing vehicle, the departure is stored with the VIN for future matching. When the vehicle is later created in Flipturn with that VIN, the next sync call will automatically link the departures to the vehicle.

Request:

```sh
curl -X POST "https://api.getflipturn.com/api/departure-times/vehicle" \
-H "Authorization: Bearer {token}" \
-H "Content-Type: application/json" \
-d '{
  "vehicleVin": "NEW_VIN_12345",
  "departureTimes": [
    {
      "departureTime": "2026-02-05T07:00:00-05:00",
      "shiftName": "Early Shift"
    }
  ]
}'
```

Response (`vehicleId` null, `vehicleVin` set):

```js
{
  "departureTimes": [
    {
      "id": 1004,
      "vehicleId": null,
      "vehicleVin": "NEW_VIN_12345",
      "departureTime": "2026-02-05T12:00:00.000Z",
      "shiftName": "Early Shift",
      "routeName": null,
      "targetSoc": null
    }
  ]
}
```

### Example: clearing all future departures

To remove all future departures for a vehicle, send an empty array:

```sh
curl -X POST "https://api.getflipturn.com/api/departure-times/vehicle" \
-H "Authorization: Bearer {token}" \
-H "Content-Type: application/json" \
-d '{ "vehicleId": 123, "departureTimes": [] }'
```

Response:

```js
{ "departureTimes": [] }
```

### Validation errors

| Condition                                     | Status |
| --------------------------------------------- | ------ |
| Neither `vehicleId` nor `vehicleVin` provided | 400    |
| Both `vehicleId` and `vehicleVin` provided    | 400    |
| `vehicleId` does not exist in the org         | 404    |
| `targetSoc` outside 0–100                     | 400    |
| Non-org-scoped API key                        | 400    |

***

## POST /api/departure-times/batch

Sync departure times for **multiple vehicles** in a single request. Each vehicle is processed independently — if one vehicle identifier is invalid, the others still succeed.

The request body is an array of vehicle sync items. Each item follows the same rules as the single-vehicle endpoint above.

### Request Body

```ts
{
  vehicles: Array<{
    vehicleVin?: string;   // Either VIN or vehicle ID must be provided per item
    vehicleId?: number;
    departureTimes: {
      departureTime: string;   // (required) ISO 8601 datetime with optional timezone offset
      shiftName?: string;
      routeName?: string;
      targetSoc?: number;      // 0–100, defaults to 90
    }[];
  }>;
}
```

### Response

Each item in the `results` array corresponds to one input vehicle (in order). On success the departure times are included with per-item `outcome`; on error an `error` message is included instead.

```ts
{
  results: Array<{
    vehicleId: number | null;
    vehicleVin: string | null;
    outcome: string;              // "success" | "error"
    departureTimes?: {           // Present when outcome is "success"
      departureTime: string;
      outcome: string;           // "created" | "skipped"
      id: number | null;
      vehicleId: number | null;
      vehicleVin: string | null;
      shiftName: string | null;
      routeName: string | null;
      targetSoc: number | null;
    }[];
    error?: string;              // Present when outcome is "error"
  }>;
}
```

### Example request and response

```sh
curl -X POST "https://api.getflipturn.com/api/departure-times/batch" \
-H "Authorization: Bearer {token}" \
-H "Content-Type: application/json" \
-d '{
  "vehicles": [
    {
      "vehicleId": 123,
      "departureTimes": [
        { "departureTime": "2026-02-05T06:00:00-05:00", "shiftName": "AM Run", "targetSoc": 90 }
      ]
    },
    {
      "vehicleId": 999999,
      "departureTimes": [
        { "departureTime": "2026-02-05T06:00:00-05:00" }
      ]
    },
    {
      "vehicleVin": "1HGBH41JXMN109186",
      "departureTimes": [
        { "departureTime": "2026-02-05T14:00:00-05:00", "shiftName": "PM Run" }
      ]
    }
  ]
}'
```

Response (second vehicle failed, others succeeded):

```js
{
  "results": [
    {
      "vehicleId": 123,
      "vehicleVin": null,
      "outcome": "success",
      "departureTimes": [
        {
          "id": 1001,
          "vehicleId": 123,
          "vehicleVin": null,
          "departureTime": "2026-02-05T11:00:00.000Z",
          "shiftName": "AM Run",
          "routeName": null,
          "targetSoc": 90
        }
      ]
    },
    {
      "vehicleId": 999999,
      "vehicleVin": null,
      "outcome": "error",
      "error": "Vehicle with id 999999 not found"
    },
    {
      "vehicleId": 456,
      "vehicleVin": null,
      "outcome": "success",
      "departureTimes": [
        {
          "id": 1002,
          "vehicleId": 456,
          "vehicleVin": null,
          "departureTime": "2026-02-05T19:00:00.000Z",
          "shiftName": "PM Run",
          "routeName": null,
          "targetSoc": 90
        }
      ]
    }
  ]
}
```
