# Acuity Scheduling

Lightweight, dependency-free, in-memory Acuity Scheduling API fake for testing scheduling code.

Default port: `4850`

## Quick start

```js
import { AcuitySchedulingServer } from "./services/acuity-scheduling/src/server.js";

const server = new AcuitySchedulingServer(4850);
await server.start();
// ... run your app/tests ...
await server.stop();
```

Point an Acuity client at `http://127.0.0.1:4850`. Authenticate with HTTP Basic
auth using `userId:apiKey` (any non-empty Basic credentials accepted):

```js
const basic = Buffer.from("parlel:parlel").toString("base64");
const res = await fetch("http://127.0.0.1:4850/api/v1/me", {
  headers: { Authorization: `Basic ${basic}` },
});
```

## Implemented operations

All `/api/v1/*` routes require Basic auth. State is in-memory. Appointment shape:

```json
{ "id": 1000001, "firstName": "Alice", "lastName": "Smith", "email": "alice@parlel.dev",
  "datetime": "2024-06-01T09:00:00-0000", "type": "Initial Consultation", "appointmentTypeID": 1,
  "duration": "30", "price": "0.00", "canceled": false, ... }
```

- `GET /api/v1/me` — the authenticated account.
- `GET /api/v1/appointment-types` — list appointment types.
- `GET /api/v1/availability/dates?month=YYYY-MM` — available dates.
- `GET /api/v1/availability/times?date=&appointmentTypeID=` — available times.
- `GET /api/v1/appointments` — list appointments.
- `POST /api/v1/appointments` — create an appointment (`datetime` + `appointmentTypeID` required).
- `GET /api/v1/appointments/:id` — retrieve an appointment.
- `PUT /api/v1/appointments/:id` — update fields.
- `PUT /api/v1/appointments/:id/cancel` — cancel (`canceled: true`).

### Service & inspection (parlel extensions)

- `GET /` — service metadata.
- `GET /health` — health check.
- `POST /__parlel/reset` — reset state.
- `OPTIONS *` — CORS preflight (`204`).

## Access via MCP / preview URL

The emulator is reachable at `ACUITY_SCHEDULING_BASE_URL`
(`http://127.0.0.1:4850`). When running in the parlel pool, an MCP tool /
preview URL proxies to this base URL — point your Acuity client at that URL with
Basic auth and every `/api/v1/*` endpoint above works as documented.

## Surface coverage

This emulator faithfully replicates the API surface most application code and agents exercise. Anything below the supported lines is either an intentional design choice for a fast, zero-cost local emulator (✓ By design) or a candidate for a future release (⟳ Roadmap) — never a silent inaccuracy.

Legend: ✅ fully supported · ◐ accepted (stored, not strictly enforced) · ✓ by design · ⟳ on the roadmap.

| Feature | Status |
| --- | --- |
| `GET /api/v1/me` | ✅ Supported |
| Appointment types | ✅ Supported |
| Availability dates/times | ✅ Supported (deterministic) |
| Appointments list/create/get/update/cancel | ✅ Supported |
| Basic auth (`userId:apiKey`) | ✅ Supported |
| OAuth flow | ◐ Out of scope (Basic accepted) |
| Forms / certificates / blocks / clients | ⟳ Roadmap |
| Real availability computation | ◐ Static dates/times |
| Credential validity | ✓ By design — Intentional for a local, zero-cost test emulator |
| Rate limiting (`429`) | ✓ By design — Never throttles — local tests run at full speed, zero cost |

## Error codes & shapes

Errors use `{ status_code, message, error }`:

| Status | When |
| --- | --- |
| `400` | appointment missing `datetime`/`appointmentTypeID` |
| `401` | missing Basic credentials |
| `404` | unknown appointment or endpoint |

## Manifest

See `services/acuity-scheduling/manifest.json`:

- name: `acuity-scheduling`, port: `4850`, protocol: `http`, healthcheck: `/health`, startup ≈ 100ms
- env: `ACUITY_SCHEDULING_USER_ID`, `ACUITY_SCHEDULING_API_KEY`, `ACUITY_SCHEDULING_BASE_URL`
