# FRT Connect API

Connect apps, automations, and AI agents to Flat Rate Tracker.

FRT Connect provides authenticated programmatic access to supported Flat Rate Tracker functionality for the signed-in user’s own account. It is a **Pro** feature.

**Base URL**

```text
https://api.flatratetracker.com/connect/v1
```

All FRT Connect V1 endpoints use this base URL and authenticate with an FRT Connect API key.

## Supported in V1

- List and search saved Jobs
- Create Work Orders
- Retrieve Work Orders

V1 does **not** include Scan RO, billing, Team management, PayTrack, ToolTrack, attachments/photos, webhooks, or a public developer portal.

## Getting started

1. Sign in to Flat Rate Tracker.
2. Open **Account → API Access**.
3. Create an API key (requires Pro).
4. Copy the full key when it is shown and store it securely.
5. Send the key as an `Authorization: Bearer` token.
6. Call endpoints under the Connect V1 base URL above.

The complete key is shown **only once** at creation. Listing keys later never reveals the secret.

## Authentication

```http
Authorization: Bearer frt_live_example.EXAMPLE_SECRET
```

Use your real key in place of the example. The example credential above is fictional and will not work.

### Key format

```text
frt_live_<publicId>.<secret>
```

### Important

- Treat API keys like passwords.
- Store them server-side or in a secrets manager.
- Never commit keys to source control.
- Never embed keys in publicly distributed client-side code.
- The full credential cannot be retrieved again after creation.
- Revoke a compromised key immediately.
- Prefer one key per integration when practical.

### Rotation

There is no in-place secret rotation. To rotate:

1. Create a new key.
2. Update the integration to use the new key.
3. Confirm it works.
4. Revoke the old key.

Revoked keys stop working immediately and cannot be restored.

API keys authenticate FRT Connect only. They cannot create or manage other API keys, change billing, manage Team membership, or perform account administration. Use the API key Bearer token on every Connect request — browser session cookies are not accepted.

## Saved Jobs

### `GET /jobs`

List the authenticated user’s saved Jobs.

Optional query parameters:

| Param | Description |
|---|---|
| `q` | Name search (case-insensitive). When present, returns matching saved Jobs by name. |
| `usage` | Default includes usage stats. Pass `usage=false` to omit them. |
| `sort` | When not searching: `name` (default), `used`, or `recent`. |

Example:

```http
GET /connect/v1/jobs?q=brake
Authorization: Bearer frt_live_example.EXAMPLE_SECRET
```

Example response:

```json
{
  "jobs": [
    {
      "id": "66f0aaaaaaaaaaaaaaaaaaaa",
      "name": "Brake pads",
      "hours": 1.5,
      "pay_type": "customer",
      "times_used": 12,
      "last_used_date": "20260920"
    }
  ]
}
```

Use a returned Job `id` as `saved_job_id` when creating a Work Order.

### `GET /jobs?q=<search>`

Same endpoint with `q` set. Search uses case-insensitive name matching over the user’s accessible saved Jobs.

## Create a Work Order

### `POST /work-orders`

Create a Work Order for the authenticated account. Connect uses Flat Rate Tracker’s normal Work Order creation behavior (validation, job snapshots, pay calculation, and default flagging).

Headers:

| Header | Required | Description |
|---|---|---|
| `Authorization` | yes | Bearer API key |
| `Idempotency-Key` | recommended | Unique string per intended create (max 100 characters). Safe retries return the same Work Order. |
| `Content-Type` | yes | `application/json` |

Body fields:

| Field | Type | Notes |
|---|---|---|
| `ro_number` | string | Repair Order / Work Order number |
| `date` | string | `YYYY-MM-DD` |
| `description` | string | optional short description shown in Flat Rate Tracker |
| `vehicle` | string | optional |
| `vin` | string | optional |
| `odometer` | string | optional |
| `advisor` | string | optional |
| `tag` | string | optional RO / vehicle tag identifier (preserve leading zeros; not coerced to a number) |
| `jobs` | array | Job lines (see below) |

Unknown top-level fields and unknown job properties are rejected with `UNSUPPORTED_FIELD`. Do not send internal Flat Rate Tracker fields.

Ownership is determined by the authenticated API key’s account. Clients cannot assign a Work Order to another Flat Rate Tracker user. API consumers do not send pay snapshots or expected earnings — Flat Rate Tracker calculates pay internally.

Flagging controls are not exposed in Connect V1. Work Orders created through Connect use Flat Rate Tracker’s normal creation default (fully flagged at creation, same as Quick Add).

### Job formats

**Saved Job reference**

```json
{
  "saved_job_id": "66f0aaaaaaaaaaaaaaaaaaaa",
  "pay_type": "customer"
}
```

**One-time Job**

```json
{
  "name": "Diagnosis",
  "hours": 1.0,
  "pay_type": "customer"
}
```

`jobs[].name` is the Job description/name (not the Work Order short description).

**Saved Job with name/hours override**

```json
{
  "saved_job_id": "66f0aaaaaaaaaaaaaaaaaaaa",
  "name": "Brake pads",
  "hours": 1.6,
  "pay_type": "warranty"
}
```

`pay_type` values:

- `customer`
- `warranty`
- `internal`

These map to Flat Rate Tracker’s existing labor types.

Each job line must include either a `saved_job_id` or a `name` and `hours`.

### Example create request

```http
POST /connect/v1/work-orders
Authorization: Bearer frt_live_example.EXAMPLE_SECRET
Idempotency-Key: agent-run-7f3c-ro-54821
Content-Type: application/json
```

```json
{
  "ro_number": "54821",
  "date": "2026-09-29",
  "description": "Customer waiting",
  "vehicle": "2022 Toyota Camry",
  "vin": "1FTFW1E57MFA18462",
  "odometer": "64218",
  "advisor": "Mike",
  "tag": "A17",
  "jobs": [
    {
      "saved_job_id": "66f0aaaaaaaaaaaaaaaaaaaa",
      "pay_type": "customer"
    },
    {
      "name": "Diagnosis",
      "hours": 0.8,
      "pay_type": "internal"
    }
  ]
}
```

Example `201` response:

```json
{
  "work_order": {
    "id": "66f0cccccccccccccccccccc",
    "ro_number": "54821",
    "date": "2026-09-29",
    "description": "Customer waiting",
    "vehicle": "2022 Toyota Camry",
    "vin": "1FTFW1E57MFA18462",
    "odometer": "64218",
    "advisor": "Mike",
    "tag": "A17",
    "booked_hours": 2.3,
    "flagged_hours": 2.3,
    "jobs": [
      {
        "name": "Brake pads",
        "hours": 1.5,
        "pay_type": "customer",
        "saved_job_id": "66f0aaaaaaaaaaaaaaaaaaaa"
      },
      {
        "name": "Diagnosis",
        "hours": 0.8,
        "pay_type": "internal",
        "saved_job_id": null
      }
    ],
    "created_at": "2026-09-29T23:10:00.000Z"
  }
}
```

## Retrieve a Work Order

### `GET /work-orders/:id`

Fetch a Work Order owned by the authenticated account. Other accounts’ Work Order IDs return `404`. Manager/Team peek is not available through Connect.

```http
GET /connect/v1/work-orders/66f0cccccccccccccccccccc
Authorization: Bearer frt_live_example.EXAMPLE_SECRET
```

The response uses the same `work_order` object shape as create.

## Idempotency

Send an `Idempotency-Key` header when creating Work Orders.

```http
Idempotency-Key: agent-run-7f3c-ro-54821
```

Generate a unique Idempotency-Key for each **intended** Work Order creation.

If a request may have succeeded but the client is unsure (timeout, network failure), retry the **same** Work Order request with the **same** Idempotency-Key.

### Behavior

| Situation | Result |
|---|---|
| Same Idempotency-Key + same/equivalent Work Order request | Returns the originally created Work Order. Does **not** create a duplicate. |
| Same Idempotency-Key + different Work Order request | `409 Conflict` with `IDEMPOTENCY_KEY_REUSE` |
| Concurrent identical requests with the same key | At most one Work Order is created |

Example conflict response:

```json
{
  "error": {
    "code": "IDEMPOTENCY_KEY_REUSE",
    "message": "This Idempotency-Key was already used with a different request."
  },
  "code": "IDEMPOTENCY_KEY_REUSE",
  "message": "This Idempotency-Key was already used with a different request."
}
```

Do **not** use the RO number itself as the Idempotency-Key unless you can guarantee the semantics you need. Prefer a generated unique request identifier. RO numbers are not globally unique.

## Errors

Connect returns JSON with a stable `code` nested under `error` and mirrored at the top level:

```json
{
  "error": {
    "code": "INVALID_API_KEY",
    "message": "Invalid API key."
  },
  "code": "INVALID_API_KEY",
  "message": "Invalid API key."
}
```

| Status | Codes |
|---|---|
| `400` | `MALFORMED_REQUEST`, `UNSUPPORTED_FIELD`, `INVALID_IDEMPOTENCY_KEY` |
| `401` | `API_KEY_REQUIRED`, `INVALID_API_KEY` (includes missing, malformed, invalid, and revoked keys) |
| `402` | `payment_required` (inactive trial/subscription when writing) |
| `403` | `capability_required` (missing Pro / Connect entitlement), `not_tracked_technician`, `CONNECT_ACCESS_DISABLED` |
| `404` | `WORK_ORDER_NOT_FOUND`, `UNKNOWN_SAVED_JOB` |
| `409` | `IDEMPOTENCY_KEY_REUSE` |
| `422` | `INVALID_WORK_ORDER`, `INVALID_JOB`, `INVALID_PAY_TYPE`, invalid hours/date |
| `429` | `RATE_LIMITED` |

`CONNECT_ACCESS_DISABLED` means Connect access for the account has been disabled independently of the user’s normal Flat Rate Tracker login, subscription, and app access. Existing API keys are not deleted when Connect is disabled.

## Rate limits

Applied only to `/connect/v1`:

- **120 requests per minute per API key**
- **180 requests per minute per IP**

Exceeding a limit returns `429` with `RATE_LIMITED`. Responses include a `Retry-After` header (seconds).

Limits protect service reliability. There is no daily quota in V1.

Flat Rate Tracker may rate-limit or disable Connect access when necessary to protect the service. Disabling Connect blocks `/connect/v1` for that account only and does **not** suspend the user’s Flat Rate Tracker account, subscription, or normal app access.

## Pro entitlement

FRT Connect requires Pro-level entitlement for Connect.

- Creating keys in Account → API Access requires Pro.
- Every `/connect/v1` request requires Connect entitlement.
- List and revoke remain available after a downgrade so keys can be cleaned up.
- Existing keys are not deleted or revoked when Pro ends; Connect requests stop until eligible entitlement returns.
- Work Order writes also keep Flat Rate Tracker’s normal active-entitlement and tracked-technician rules.

## Security and key management

- Use one API key per integration.
- Give keys descriptive names.
- Store secrets server-side.
- Never commit credentials.
- Revoke exposed credentials immediately.
- Replace credentials when rotating or retiring an integration.
- Remove keys for integrations no longer in use.

Revoked credentials cannot be restored. Create a new key and revoke the previous one to rotate.

## Machine-readable copy

The same documentation is available as Markdown at:

```text
https://flatratetracker.com/connect.md
```
