# Quickstart

From nothing to a schema-validated record you created and read back, in about
ten minutes. One linear path, no branching. All you need is a terminal with
`curl` (examples also use `jq`, but it's optional).

If you want the *why* behind any step, the [core concepts](/docs/concepts.html)
page explains the model; this page just moves.

---

## 1. Get your credentials

Platform Lydian is operator-provisioned: you don't sign yourself up. The
platform operator gives you two values, out of band:

- **Tenant id** — a UUID naming your isolated namespace, e.g.
  `019fb5a2-9626-7ba6-ad59-b5f98cf04c8e`. An identifier, not a secret.
- **API key** — `lyd_` followed by 43 characters. Shown once at issue time,
  never recoverable — if it's lost the operator rotates it, which invalidates
  the old key immediately.

The key has full read/write authority over your whole tenant. Keep it
server-side: never in a browser bundle, a mobile app, or a repository.

Set up your shell (the rest of this page assumes these three variables):

```bash
export LYDIAN=https://platformlydian.com/dev-api
export TENANT=YOUR_TENANT_ID
export KEY=YOUR_API_KEY
```

`/dev-api` is the development environment — a completely separate deployment
from production (`/api`), with separate data and separate keys. Start on dev.

## 2. Make an authenticated request

Every request carries the key in the `X-API-Key` header. List your people:

```bash
curl -s -H "X-API-Key: $KEY" "$LYDIAN/tenants/$TENANT/people" | jq
```

A fresh tenant answers with an empty list:

```json
[]
```

That empty array is a successful, authenticated, tenant-scoped call — you're
in. If instead you get `401` with `{"code": "UNAUTHENTICATED", …}`, the header
is missing or the key is wrong; `403` with `{"code": "FORBIDDEN", …}` means
the key is valid but the tenant id in the URL isn't yours.

## 3. Define a minimal schema

Records are validated against tenant-defined types composed from attributes.
Build the smallest possible schema: one attribute, one type.

Create an **attribute** — "Instrument", a required string. The `type` object
is polymorphic; `kind` selects the flavor:

```bash
curl -s -X POST "$LYDIAN/tenants/$TENANT/attributes" \
  -H "X-API-Key: $KEY" -H "Content-Type: application/json" \
  -d '{
        "name": "Instrument",
        "description": "The instrument this student plays",
        "type": {"kind": "string", "required": true, "maxLength": 40}
      }' | jq
```

```json
{
  "id": "0198c0de-3a51-7c2e-9f00-1f4a2b6c8d01",
  "tenantId": "019fb5a2-9626-7ba6-ad59-b5f98cf04c8e",
  "name": "Instrument",
  "description": "The instrument this student plays",
  "type": {"kind": "string", "required": true, "maxLength": 40},
  "version": {"major": 1, "minor": 0}
}
```

(`name` **and** `description` are required — on attributes and on every type.
Omitting either is a `400 VALIDATION_FAILED`.)

Save the id, then compose a **people type** that uses it:

```bash
export INSTRUMENT=0198c0de-3a51-7c2e-9f00-1f4a2b6c8d01   # your id from above

curl -s -X POST "$LYDIAN/tenants/$TENANT/people-types" \
  -H "X-API-Key: $KEY" -H "Content-Type: application/json" \
  -d "{
        \"name\": \"Student\",
        \"description\": \"A music student\",
        \"attributeIds\": [\"$INSTRUMENT\"]
      }" | jq
```

```json
{
  "id": "0198c0e1-88b4-7d3a-bf21-6a0f5f9e2c17",
  "tenantId": "019fb5a2-9626-7ba6-ad59-b5f98cf04c8e",
  "name": "Student",
  "description": "A music student",
  "version": {"major": 1, "minor": 0},
  "attributeIds": ["0198c0de-3a51-7c2e-9f00-1f4a2b6c8d01"],
  "base": false,
  "baseFields": [
    {"name": "firstName", "kind": "string", "required": true},
    {"name": "lastName",  "kind": "string", "required": true},
    {"name": "email",     "kind": "email",  "required": true}
  ]
}
```

`baseFields` lists what every person has regardless of type; your attribute
rides on top of those.

## 4. Create a record

Create a person of type Student. Note that `attributeValues` is keyed by the
attribute's **id**, not its name — the most common first-day mistake:

```bash
export STUDENT=0198c0e1-88b4-7d3a-bf21-6a0f5f9e2c17     # your id from above

curl -s -X POST "$LYDIAN/tenants/$TENANT/people" \
  -H "X-API-Key: $KEY" -H "Content-Type: application/json" \
  -d "{
        \"firstName\": \"Ada\",
        \"lastName\": \"Lovelace\",
        \"email\": \"ada@example.com\",
        \"peopleTypeId\": \"$STUDENT\",
        \"attributeValues\": {\"$INSTRUMENT\": \"cello\"}
      }" | jq
```

```json
{
  "id": "0198c0e5-1f60-7e88-a3d4-92c7b81e6f42",
  "tenantId": "019fb5a2-9626-7ba6-ad59-b5f98cf04c8e",
  "firstName": "Ada",
  "lastName": "Lovelace",
  "email": "ada@example.com",
  "peopleTypeId": "0198c0e1-88b4-7d3a-bf21-6a0f5f9e2c17",
  "peopleTypeVersion": {"major": 1, "minor": 0},
  "attributeValues": {"0198c0de-3a51-7c2e-9f00-1f4a2b6c8d01": "cello"}
}
```

The record was validated against the Student type on the way in: a missing
value for a required attribute is a `400`, and so is a value for an attribute
the type doesn't declare. `peopleTypeVersion` snapshots the schema version the
record was written under.

## 5. Read it back

```bash
curl -s -H "X-API-Key: $KEY" "$LYDIAN/tenants/$TENANT/people" | jq
```

The list that was `[]` in step 2 now returns Ada, exactly as created. You can
also fetch her directly — replace the id with yours from step 4:

```bash
curl -s -H "X-API-Key: $KEY" \
  "$LYDIAN/tenants/$TENANT/people/0198c0e5-1f60-7e88-a3d4-92c7b81e6f42" | jq
```

That's the whole loop: authenticate, define a schema, create a validated
record, read it back. Every other record kind — events, documents, invoices,
communications — works on exactly this pattern; only the base fields and the
type endpoint change.

---

## Where to go next

- **[Core concepts](/docs/concepts.html)** — the mental model behind what you
  just did, and how the five record kinds interconnect.
- **[Authentication & conventions](/docs/conventions.html)** — the error
  envelope, timestamps, and platform limits you'll hit next.
- **How-to guides** — [model your domain](/docs/model-your-domain.html),
  [manage a document](/docs/manage-a-document.html),
  [invoice from events](/docs/invoice-from-events.html),
  [work with the inbox](/docs/work-with-the-inbox.html).
- **[API reference](/docs/)** — or feed the raw spec at
  [/openapi.yml](/openapi.yml) to your client generator or AI agent.
