Lydira API
TR

API reference

Customers

7 endpoints

The agency's client records, plus the note thread on each one. name, email, phone and the tax fields are personal data: a key whose role cannot see PII reads them as null, and a customer carrying a restricted tag has its identity masked as well, so a row is never withheld outright, only emptied.

Notes accept attachments as Active Storage signed_ids. Upload the file first, then send the ids.

List customers

GET/api/v1/customers

Needs a key with the full scope.

Newest first. Soft-deleted customers are never returned.

Parameters

Query

  • qstring

    Matches name, email or phone.

  • company_iduuid

    From GET /api/v1/me.

  • vip_tierstring
  • lifecycle_stagestring
  • limitintegerdefault 25

    Rows per page.

  • cursorstring

    Opaque; take it from the previous response's page.next_cursor.

Response · 200

  • dataobject[]required
    Show fields
    • iduuid
    • namestringnullable
    • customer_typestring
    • emailstringnullable
    • phonestringnullable
    • lifecycle_stagestringnullable
    • vip_tierstringnullable
    • activeboolean
    • tagsstring[]
    • company_iduuid
  • pageobjectrequired

    The cursor for the list beside it. Re-send the same request with ?cursor=<next_cursor> while has_more is true. next_cursor is null on the last page.

    Show fields
    • next_cursorstringrequirednullable
    • has_morebooleanrequired
    • limitintegerrequired

Responses

  • 401Missing or invalid Bearer token
  • 403Policy denied or account has no owner
  • 429Rate limit exceeded
curl -X GET 'https://YOUR-CELL.lydira.com/api/v1/customers' \
  -H 'Authorization: Bearer sek_YOUR_API_KEY'
Response
200
{
  "data": [
    {
      "id": "9d2f7c3a-1b4e-4a7d-8e51-2c0f6b9a4d18",
      "name": "Ayşe Yılmaz",
      "customer_type": "individual",
      "email": "ayse@example.com",
      "phone": "+90 532 111 22 33",
      "lifecycle_stage": "active",
      "vip_tier": "gold",
      "active": true,
      "tags": [
        "vip",
        "repeat"
      ],
      "company_id": "9d2f7c3a-1b4e-4a7d-8e51-2c0f6b9a4d18"
    }
  ],
  "page": {
    "next_cursor": "MjAyNi0wMy0xNFQwOTozMDowMC4wMDAwMDBafDk0MQ",
    "has_more": true,
    "limit": 25
  }
}

Create a customer

POST/api/v1/customers

Needs a key with the full scope.

company_id defaults to the key owner's home company when omitted, so a single-company agency can leave it out entirely.

This endpoint has no external_id and no deduplication. To push customers from another system and keep your own ids, use POST /api/v1/imports/customers instead, which upserts.

Parameters

Headers

  • Idempotency-Keystring

    Your own unique string for this write (a UUID is the obvious choice), max 255 characters. Optional: omit it and nothing changes. Send it and a repeat of the same request returns this call's response verbatim, with Idempotent-Replay: true, instead of writing again.

Body

  • customerobjectrequired
    Show fields
    • namestring
    • first_namestring
    • last_namestring
    • emailemail
    • phonestring
    • customer_typestring
    • legal_namestring
    • tax_numberstring
    • tax_officestring
    • tax_office_codestring
    • birthdaydate
    • anniversarydate
    • vip_tierstring
    • lifecycle_stagestring
    • preferred_channelstring
    • opted_in_marketingboolean
    • codestring
    • activeboolean
    • company_iduuid
    • addressobject
    • tagsstring[]

      Replaces the whole set. Omit the key to leave tags alone.

Response · 201

  • dataobject

    List shape. name, email and phone are personal data: a key whose role cannot read PII gets null, and a customer carrying a restricted tag has its name masked too.

    Show fields
    • iduuid
    • namestringnullable
    • customer_typestring
    • emailstringnullable
    • phonestringnullable
    • lifecycle_stagestringnullable
    • vip_tierstringnullable
    • activeboolean
    • tagsstring[]
    • company_iduuid
    • first_namestringnullable
    • last_namestringnullable
    • legal_namestringnullable
    • tax_numberstringnullable
    • tax_officestringnullable
    • tax_office_codestringnullable
    • tax_schemestringnullable
    • addressobjectnullable

      Free-form address object.

    • birthdaydatenullable
    • anniversarydatenullable
    • preferred_channelstringnullable
    • opted_in_marketingboolean
    • opted_in_atdate-timenullable
    • codestringnullable
    • created_atdate-time
    • updated_atdate-time

Responses

  • 401Missing or invalid Bearer token
  • 403Policy denied or account has no owner
  • 422The request was understood but the record could not be written. details maps each rejected attribute to its reasons, so a client can put the message next to the field that caused it.
  • 429Rate limit exceeded
curl -X POST 'https://YOUR-CELL.lydira.com/api/v1/customers' \
  -H 'Authorization: Bearer sek_YOUR_API_KEY' \
  -H 'Content-Type: application/json' \
  -H 'Idempotency-Key: 8f14e45f-ea2c-4f33-9a3b-6d0c1b7e5a90' \
  -d '{
  "customer": {
    "name": "Ayşe Yılmaz",
    "first_name": "Ayşe",
    "last_name": "Yılmaz",
    "email": "ayse@example.com",
    "phone": "+90 532 111 22 33",
    "customer_type": "individual"
  }
}'
Response
201
{
  "data": {
    "id": "9d2f7c3a-1b4e-4a7d-8e51-2c0f6b9a4d18",
    "name": "Ayşe Yılmaz",
    "customer_type": "individual",
    "email": "ayse@example.com",
    "phone": "+90 532 111 22 33",
    "lifecycle_stage": "active",
    "vip_tier": "gold",
    "active": true,
    "tags": [
      "vip",
      "repeat"
    ],
    "company_id": "9d2f7c3a-1b4e-4a7d-8e51-2c0f6b9a4d18",
    "first_name": "Ayşe",
    "last_name": "Yılmaz",
    "legal_name": "Yılmaz Turizm A.Ş.",
    "tax_number": "1234567890",
    "tax_office": "Beşiktaş",
    "tax_office_code": "034255",
    "tax_scheme": "standard",
    "address": {},
    "birthday": "2026-03-14",
    "anniversary": "2026-03-14",
    "preferred_channel": "email",
    "opted_in_marketing": true,
    "opted_in_at": "2026-03-14T09:30:00Z",
    "code": "CUST-0042",
    "created_at": "2026-03-14T09:30:00Z",
    "updated_at": "2026-03-14T09:30:00Z"
  }
}

Read a customer

GET/api/v1/customers/{id}

Needs a key with the full scope.

Parameters

Path

  • iduuidrequired

Response · 200

  • dataobject

    List shape. name, email and phone are personal data: a key whose role cannot read PII gets null, and a customer carrying a restricted tag has its name masked too.

    Show fields
    • iduuid
    • namestringnullable
    • customer_typestring
    • emailstringnullable
    • phonestringnullable
    • lifecycle_stagestringnullable
    • vip_tierstringnullable
    • activeboolean
    • tagsstring[]
    • company_iduuid
    • first_namestringnullable
    • last_namestringnullable
    • legal_namestringnullable
    • tax_numberstringnullable
    • tax_officestringnullable
    • tax_office_codestringnullable
    • tax_schemestringnullable
    • addressobjectnullable

      Free-form address object.

    • birthdaydatenullable
    • anniversarydatenullable
    • preferred_channelstringnullable
    • opted_in_marketingboolean
    • opted_in_atdate-timenullable
    • codestringnullable
    • created_atdate-time
    • updated_atdate-time

Responses

  • 401Missing or invalid Bearer token
  • 403Policy denied or account has no owner
  • 404Resource not found
  • 429Rate limit exceeded
curl -X GET 'https://YOUR-CELL.lydira.com/api/v1/customers/9d2f7c3a-1b4e-4a7d-8e51-2c0f6b9a4d18' \
  -H 'Authorization: Bearer sek_YOUR_API_KEY'
Response
200
{
  "data": {
    "id": "9d2f7c3a-1b4e-4a7d-8e51-2c0f6b9a4d18",
    "name": "Ayşe Yılmaz",
    "customer_type": "individual",
    "email": "ayse@example.com",
    "phone": "+90 532 111 22 33",
    "lifecycle_stage": "active",
    "vip_tier": "gold",
    "active": true,
    "tags": [
      "vip",
      "repeat"
    ],
    "company_id": "9d2f7c3a-1b4e-4a7d-8e51-2c0f6b9a4d18",
    "first_name": "Ayşe",
    "last_name": "Yılmaz",
    "legal_name": "Yılmaz Turizm A.Ş.",
    "tax_number": "1234567890",
    "tax_office": "Beşiktaş",
    "tax_office_code": "034255",
    "tax_scheme": "standard",
    "address": {},
    "birthday": "2026-03-14",
    "anniversary": "2026-03-14",
    "preferred_channel": "email",
    "opted_in_marketing": true,
    "opted_in_at": "2026-03-14T09:30:00Z",
    "code": "CUST-0042",
    "created_at": "2026-03-14T09:30:00Z",
    "updated_at": "2026-03-14T09:30:00Z"
  }
}

Update a customer

PATCH/api/v1/customers/{id}

Needs a key with the full scope.

Partial: send only the fields you are changing. PUT is routed to the same action and behaves identically.

tags is all-or-nothing. Omit the key to leave tags untouched; send an array to replace the whole set.

Parameters

Path

  • iduuidrequired

Headers

  • Idempotency-Keystring

    Your own unique string for this write (a UUID is the obvious choice), max 255 characters. Optional: omit it and nothing changes. Send it and a repeat of the same request returns this call's response verbatim, with Idempotent-Replay: true, instead of writing again.

Body

  • customerobjectrequired
    Show fields
    • namestring
    • first_namestring
    • last_namestring
    • emailemail
    • phonestring
    • customer_typestring
    • legal_namestring
    • tax_numberstring
    • tax_officestring
    • tax_office_codestring
    • birthdaydate
    • anniversarydate
    • vip_tierstring
    • lifecycle_stagestring
    • preferred_channelstring
    • opted_in_marketingboolean
    • codestring
    • activeboolean
    • company_iduuid
    • addressobject
    • tagsstring[]

      Replaces the whole set. Omit the key to leave tags alone.

Response · 200

  • dataobject

    List shape. name, email and phone are personal data: a key whose role cannot read PII gets null, and a customer carrying a restricted tag has its name masked too.

    Show fields
    • iduuid
    • namestringnullable
    • customer_typestring
    • emailstringnullable
    • phonestringnullable
    • lifecycle_stagestringnullable
    • vip_tierstringnullable
    • activeboolean
    • tagsstring[]
    • company_iduuid
    • first_namestringnullable
    • last_namestringnullable
    • legal_namestringnullable
    • tax_numberstringnullable
    • tax_officestringnullable
    • tax_office_codestringnullable
    • tax_schemestringnullable
    • addressobjectnullable

      Free-form address object.

    • birthdaydatenullable
    • anniversarydatenullable
    • preferred_channelstringnullable
    • opted_in_marketingboolean
    • opted_in_atdate-timenullable
    • codestringnullable
    • created_atdate-time
    • updated_atdate-time

Responses

  • 401Missing or invalid Bearer token
  • 403Policy denied or account has no owner
  • 404Resource not found
  • 422The request was understood but the record could not be written. details maps each rejected attribute to its reasons, so a client can put the message next to the field that caused it.
  • 429Rate limit exceeded
curl -X PATCH 'https://YOUR-CELL.lydira.com/api/v1/customers/9d2f7c3a-1b4e-4a7d-8e51-2c0f6b9a4d18' \
  -H 'Authorization: Bearer sek_YOUR_API_KEY' \
  -H 'Content-Type: application/json' \
  -H 'Idempotency-Key: 8f14e45f-ea2c-4f33-9a3b-6d0c1b7e5a90' \
  -d '{
  "customer": {
    "name": "Ayşe Yılmaz",
    "first_name": "Ayşe",
    "last_name": "Yılmaz",
    "email": "ayse@example.com",
    "phone": "+90 532 111 22 33",
    "customer_type": "individual"
  }
}'
Response
200
{
  "data": {
    "id": "9d2f7c3a-1b4e-4a7d-8e51-2c0f6b9a4d18",
    "name": "Ayşe Yılmaz",
    "customer_type": "individual",
    "email": "ayse@example.com",
    "phone": "+90 532 111 22 33",
    "lifecycle_stage": "active",
    "vip_tier": "gold",
    "active": true,
    "tags": [
      "vip",
      "repeat"
    ],
    "company_id": "9d2f7c3a-1b4e-4a7d-8e51-2c0f6b9a4d18",
    "first_name": "Ayşe",
    "last_name": "Yılmaz",
    "legal_name": "Yılmaz Turizm A.Ş.",
    "tax_number": "1234567890",
    "tax_office": "Beşiktaş",
    "tax_office_code": "034255",
    "tax_scheme": "standard",
    "address": {},
    "birthday": "2026-03-14",
    "anniversary": "2026-03-14",
    "preferred_channel": "email",
    "opted_in_marketing": true,
    "opted_in_at": "2026-03-14T09:30:00Z",
    "code": "CUST-0042",
    "created_at": "2026-03-14T09:30:00Z",
    "updated_at": "2026-03-14T09:30:00Z"
  }
}

List notes on a customer

GET/api/v1/customers/{id}/notes

Needs a key with the full scope.

Pinned notes first, then newest first. Bounded, so there is no page.

Parameters

Path

  • iduuidrequired

Response · 200

  • dataobject[]required
    Show fields
    • iduuid
    • bodystring
    • pinnedboolean
    • pinned_atdate-timenullable
    • authorobjectnullable

      A seat on the account, as referenced from other records.

      Show fields
      • iduuid
      • namestringnullable
      • emailstringnullable
    • attachments_countinteger
    • attachmentsobject[]

      Detail only. url is short-lived; fetch it again rather than storing it.

      Show fields
      • idstring
      • filenamestring
      • content_typestringnullable
      • byte_sizeinteger
      • urlstring
    • created_atdate-time
    • updated_atdate-time

Responses

  • 401Missing or invalid Bearer token
  • 403Policy denied or account has no owner
  • 404Resource not found
  • 429Rate limit exceeded
curl -X GET 'https://YOUR-CELL.lydira.com/api/v1/customers/9d2f7c3a-1b4e-4a7d-8e51-2c0f6b9a4d18/notes' \
  -H 'Authorization: Bearer sek_YOUR_API_KEY'
Response
200
{
  "data": [
    {
      "id": "9d2f7c3a-1b4e-4a7d-8e51-2c0f6b9a4d18",
      "body": "Guest asked for a late checkout.",
      "pinned": true,
      "pinned_at": "2026-03-14T09:30:00Z",
      "author": {
        "id": "9d2f7c3a-1b4e-4a7d-8e51-2c0f6b9a4d18",
        "name": "Ayşe Yılmaz",
        "email": "ayse@example.com"
      },
      "attachments_count": 3,
      "attachments": [
        {
          "id": "string",
          "filename": "voucher.pdf",
          "content_type": "application/pdf",
          "byte_size": 1,
          "url": "https://example.com/hook"
        }
      ],
      "created_at": "2026-03-14T09:30:00Z",
      "updated_at": "2026-03-14T09:30:00Z"
    }
  ]
}

Add a note

POST/api/v1/customers/{id}/notes

Needs a key with the full scope.

A note is text. attachments is there for a client that already holds Active Storage signed_ids, which is how the Lydira apps attach a photo; an integration normally sends body alone. A batch that would push the account past its storage allowance is refused before the note is written.

Parameters

Path

  • iduuidrequired

Headers

  • Idempotency-Keystring

    Your own unique string for this write (a UUID is the obvious choice), max 255 characters. Optional: omit it and nothing changes. Send it and a repeat of the same request returns this call's response verbatim, with Idempotent-Replay: true, instead of writing again.

Body

  • noteobjectrequired
    Show fields
    • bodystringrequired
    • pinnedbooleandefault false
    • attachmentsstring[]

      Active Storage signed ids. Rarely used over the API; see above.

Response · 201

  • dataobject
    Show fields
    • iduuid
    • bodystring
    • pinnedboolean
    • pinned_atdate-timenullable
    • authorobjectnullable

      A seat on the account, as referenced from other records.

      Show fields
      • iduuid
      • namestringnullable
      • emailstringnullable
    • attachments_countinteger
    • attachmentsobject[]

      Detail only. url is short-lived; fetch it again rather than storing it.

      Show fields
      • idstring
      • filenamestring
      • content_typestringnullable
      • byte_sizeinteger
      • urlstring
    • created_atdate-time
    • updated_atdate-time

Responses

  • 401Missing or invalid Bearer token
  • 403Policy denied or account has no owner
  • 404Resource not found
  • 422The request was understood but the record could not be written. details maps each rejected attribute to its reasons, so a client can put the message next to the field that caused it.
  • 429Rate limit exceeded
curl -X POST 'https://YOUR-CELL.lydira.com/api/v1/customers/9d2f7c3a-1b4e-4a7d-8e51-2c0f6b9a4d18/notes' \
  -H 'Authorization: Bearer sek_YOUR_API_KEY' \
  -H 'Content-Type: application/json' \
  -H 'Idempotency-Key: 8f14e45f-ea2c-4f33-9a3b-6d0c1b7e5a90' \
  -d '{
  "note": {
    "body": "Guest asked for a late checkout.",
    "pinned": false,
    "attachments": [
      "string"
    ]
  }
}'
Response
201
{
  "data": {
    "id": "9d2f7c3a-1b4e-4a7d-8e51-2c0f6b9a4d18",
    "body": "Guest asked for a late checkout.",
    "pinned": true,
    "pinned_at": "2026-03-14T09:30:00Z",
    "author": {
      "id": "9d2f7c3a-1b4e-4a7d-8e51-2c0f6b9a4d18",
      "name": "Ayşe Yılmaz",
      "email": "ayse@example.com"
    },
    "attachments_count": 3,
    "attachments": [
      {
        "id": "string",
        "filename": "voucher.pdf",
        "content_type": "application/pdf",
        "byte_size": 1,
        "url": "https://example.com/hook"
      }
    ],
    "created_at": "2026-03-14T09:30:00Z",
    "updated_at": "2026-03-14T09:30:00Z"
  }
}

What to pitch next

GET/api/v1/customers/{customer_id}/suggestions

Needs a key with the full scope.

Ranked ideas for this customer, blended from trips similar to the ones they have taken (vector similarity) and the agency's own trip templates filtered by their cached preference profile.

Computed on demand. When the underlying service cannot answer, this returns 503 suggestions_failed rather than an empty list, so a caller can tell "nothing to suggest" from "could not look".

Parameters

Path

  • customer_iduuidrequired

Query

  • limitintegerdefault 5

Response · 200

  • dataobject[]required
    Show fields
    • kindstring

      What kind of idea this is, for example a similar trip or a template.

    • subjectobject

      The thing being suggested.

    • reasonstring

      Why it surfaced, in plain language.

    • payloadobject

      Supporting figures for the suggestion.

Responses

  • 401Missing or invalid Bearer token
  • 403Policy denied or account has no owner
  • 404Resource not found
  • 429Rate limit exceeded
  • 503The suggestion engine could not answer
curl -X GET 'https://YOUR-CELL.lydira.com/api/v1/customers/9d2f7c3a-1b4e-4a7d-8e51-2c0f6b9a4d18/suggestions' \
  -H 'Authorization: Bearer sek_YOUR_API_KEY'
Response
200
{
  "data": [
    {
      "kind": "anniversary",
      "subject": {},
      "reason": "Booked with another agency",
      "payload": {}
    }
  ]
}