Lydira API
TR

Import guides

Visa cases

One applicant's visa file, billed to the customer who pays for it.

One applicant's visa file, billed to the customer who pays for it. Send the applicant under applicant; they are saved to that customer's traveller directory. The file opens at intake and the agency moves it from there. A second open file for the same applicant, destination and visa class is refused, and the first is left as it is.

On this page

A key with one of these scopes reaches it:importimport_issue

Visa casesvisa_cases

POST/api/v1/imports/visa_cases

Create-once: re-sending the same external_id skips rather than duplicating.

Endpoints

  • validatePOST /api/v1/imports/visa_cases/validate
  • importPOST /api/v1/imports/visa_cases
  • feedGET /api/v1/imports/visa_cases/records
  • read_backGET /api/v1/imports/visa_cases/records/{external_id}

A new row must carry

destination_countryvisa_class

Sample request billed by its bill-to (no customer key)

curl -X POST 'https://YOUR-CELL.lydira.com/api/v1/imports/visa_cases/validate' \
  -H 'Authorization: Bearer sek_YOUR_API_KEY' \
  -H 'Content-Type: application/json' \
  -H 'Idempotency-Key: 8f14e45f-ea2c-4f33-9a3b-6d0c1b7e5a90' \
  -d '{
  "source_system": "crm",
  "rows": [
    {
      "external_id": "VISA-CASE-1001-B",
      "destination_country": "DE",
      "visa_class": "schengen_short",
      "passport_country": "TR",
      "priority": "standard",
      "consulate_code": "IST-DE",
      "target_submission_date": "2026-03-14",
      "currency": "EUR",
      "supplier_external_id": "SUPPLIER-1001",
      "applicant": {},
      "notes": "Imported from the source system.",
      "transaction_date": "2026-03-14",
      "invoice_date": "2026-03-18",
      "cost_lines": [
        {
          "kind": "government_fee",
          "amount": "1250.00",
          "label": "Imported line",
          "currency": "EUR",
          "vat_rate": "20",
          "tax_inclusive": true,
          "quantity": 1
        }
      ],
      "sell_lines": [
        {
          "kind": "package_price",
          "amount": "1250.00",
          "label": "Imported line",
          "currency": "EUR",
          "vat_rate": "20",
          "tax_inclusive": true,
          "quantity": 1
        }
      ],
      "bill_to_legal_name": "Lovelace Seyahat A.Ş.",
      "bill_to_tax_number": "1234567890",
      "bill_to_tax_office": "Beşiktaş",
      "bill_to_address_line": "Nispetiye Cad. No:12",
      "bill_to_city": "İstanbul",
      "bill_to_country": "TR"
    }
  ]
}'
Fields(35)
  • external_idstring
  • destination_countrystringrequired

    ISO-2 (DE, GB)

  • visa_classtaxonomyrequired

    Your account's visa_class list; it starts with:schengen_shortuk_standard_visitor

  • passport_countrystring

    ISO-2; omit it and the applicant's passport country is read

  • priorityenum

    One of:standardurgentvip

  • consulate_codestring
  • target_submission_datedate
  • currencycurrency
  • customer_external_idreference

    the cari the case bills; a case with none, here or from the bill-to, is refused

  • supplier_external_idreference
  • applicantobjectPII

    who the visa is for, saved to the paying cari's traveller directory; omit it and the cari applies for themself

    Keys

    • first_namestringPII
    • last_namestringPII
    • nationalitystring
    • national_idstringPII
    • passport_numberstringPII
    • passport_countrystring
    • passport_expirydate
    • date_of_birthdatePII
    • genderstring

      Values the app uses:MRMRSMISSMSTR

    • frequent_flyer_numberstring
  • notesstring
  • transaction_datedate

    işlem tarihi — the FX anchor for every line

  • invoice_datedate
  • custom_fieldsobject

    the account's own fields on the case, by key

  • cost_linesobject[]

    cost-side pricing lines for the "document" pricing context

    Each item

    • kindstringrequired

      Values the app uses:government_feeconsular_feevfs_service_feecouriertranslationbiometricsphotophotocopynotaryapostilleother_disbursementservice_feesupplier_refundinsurance_premium

    • amountmoneyrequired
    • labelstring
    • currencycurrency
    • vat_ratedecimal

      percent (20 ⇒ 20% KDV); omit to take the kind's default

    • tax_inclusiveboolean

      true ⇒ the amount includes VAT. A hotel stay or a charter takes one VAT mode: the lines' flags decide it, lines stating both are refused, and every line follows the booking's mode afterwards

    • quantityinteger
  • sell_linesobject[]

    sell-side pricing lines for the "document" pricing context

    Each item

    • kindstringrequired

      Values the app uses:package_pricemarkupservice_feesupplier_commissioncancellation_feeinsurance_premiumdiscountrefund_allowancegovernment_feeconsular_feevfs_service_feecouriertranslationbiometricsphotophotocopynotaryapostilleother_disbursementsupplier_refund

    • amountmoneyrequired
    • labelstring
    • currencycurrency
    • vat_ratedecimal

      percent (20 ⇒ 20% KDV); omit to take the kind's default

    • tax_inclusiveboolean

      true ⇒ the amount includes VAT. A hotel stay or a charter takes one VAT mode: the lines' flags decide it, lines stating both are refused, and every line follows the booking's mode afterwards

    • quantityinteger
  • service_feemoney

    the agency's fee for the whole booking, in its currency; split across the guests like the booking form's. Sent, the account's service-fee rules write nothing

  • service_fee_percentdecimal

    the fee as a percentage (0-100) of the booking's sale before fee and discount, instead of service_fee; split like it

  • service_fee_vat_ratedecimal

    the fee's VAT, percent (0 ⇒ exempt); omit it and the law decides, else the seller's standard rate

  • service_fee_waivedboolean

    true: the account's service-fee rules write no fee on this booking

  • bill_to_typeenum

    omit to infer: a legal name or a 10-digit tax number ⇒ corporate

    One of:corporateindividual

  • bill_to_legal_namestring
  • bill_to_tax_numbertax_numberPII

    VKN for a corporate payer, TCKN for an individual; matched digits-only

  • bill_to_tax_officestring
  • bill_to_passport_numberstringPII

    individual only; used when there is no TCKN

  • bill_to_first_namestringPII
  • bill_to_last_namestringPII
  • bill_to_emailemailPII

    stored on a NEW cari; never used to match

  • bill_to_phonephonePII

    stored on a NEW cari; never used to match

  • bill_to_address_linestringPII
  • bill_to_districtstringPII
  • bill_to_citystringPII
  • bill_to_zipstringPII
  • bill_to_countrystring

    ISO-2 code, e.g. TR

References

  • customer_external_id → customers · matched by external_id, email · when missing skip
  • supplier_external_id → suppliers · matched by external_id, code · when missing skip

Your account's own fields (custom_fields)

Each agency adds its own fields in Settings → Custom fields: a project code, a trip reason, an employee number. They differ from account to account, so this page cannot list them. Your integration asks the API for them.

GET /api/v1/imports/entities/{key} answers, under custom_fields, the fields your key's account has on this entity: record for the record itself, traveler for each traveller. Each one names its key (what the row sends), its type, whether it is required, which customers it applies_to and, for a choice, its options. Read it when you build the mapping and again when the agency says it changed a field: it is the account's live list.

Request · the account's fields

curl -X GET 'https://YOUR-CELL.lydira.com/api/v1/imports/entities/visa_cases' \
  -H 'Authorization: Bearer sek_YOUR_API_KEY'
Response · custom_fields (excerpt)
200
{
  "key": "visa_cases",
  "custom_fields": {
    "record": [
      {
        "key": "project_code",
        "label": "Project code",
        "type": "text",
        "required": "always",
        "applies_to": "all"
      },
      {
        "key": "trip_reason",
        "label": "Trip reason",
        "type": "select",
        "required": "listed_customers",
        "applies_to": "listed_customers",
        "options": [
          {
            "value": "meeting",
            "label": "Meeting"
          },
          {
            "value": "fair",
            "label": "Trade fair"
          }
        ],
        "customers": [
          {
            "id": "9d2f7c3a-1b4e-4a7d-8e51-2c0f6b9a4d18",
            "external_id": "CUST-17",
            "required": true
          }
        ]
      }
    ],
    "traveler": []
  }
}

What each type takes

typevalue in the row
text"PRJ-2026-001"
long_text"Fair delegation, second group"
number12.5
date"2026-03-14"
booleantrue
select"meeting"
multi_select["meeting", "fair"]

number takes a number or a numeric string; date an ISO date; select one option value (or its label); multi_select a list of them.

required and applies_to

  • requiredneveralwayslisted_customers

    listed_customers: required only for the customers marked required in customers.

  • applies_toalllisted_customers

    listed_customers: the field is accepted only on those customers' records.

Send the values as one JSON object by key. An unknown key, a value of the wrong type or a missing required field fails the row and names the key. On an amendment (on_conflict: update) the object merges by key: a key you leave out is kept, a key sent as null is removed. A field the agency prints on the customer's documents cannot change once the record is invoiced.

They come back the same way: under detail.custom_fields in the read-back and the webhook body (and on each passenger of a ticket). A field marked personal comes back only to a key whose role can read personal data, and never in a webhook.

Reading it back

Once the agency works the record (confirms it, numbers the tickets, invoices it) this is what GET /api/v1/imports/{entity}/records/{external_id} answers, and what every webhook delivers. detail carries what only the agency can add after your push.

GET /api/v1/imports/visa_cases/records/VISA-CASE-1001
200
{
  "entity": "visa_cases",
  "external_id": "VISA-CASE-1001",
  "source_system": "crm",
  "type": "VisaApplicationCase",
  "id": "9d2f7c3a-1b4e-4a7d-8e51-2c0f6b9a4d18",
  "created_at": "2026-03-14T09:30:00Z",
  "updated_at": "2026-03-15T11:05:00Z",
  "locked": true,
  "lock_reason": "invoiced",
  "currency": "EUR",
  "totals": {
    "cost": "1100.0",
    "sell": "1250.0"
  },
  "invoices": [
    {
      "id": "4b8e1f60-7c2d-4e9a-b3f5-0a6d2c9e8b71",
      "number": "LYD2026000000117",
      "status": "issued",
      "currency": "EUR",
      "total": "1250.0",
      "balance": "1250.0"
    }
  ],
  "detail": {
    "voucher_number": "VCH-2026-000412",
    "supplier_confirmation_no": "CNF-77821",
    "supplier": "Lovelace Travel Partners",
    "custom_fields": {
      "project_code": "PRJ-2026-001",
      "trip_reason": "meeting"
    },
    "state": "decided",
    "outcome": "approved",
    "destination_country": "DE",
    "visa_class": "schengen_short",
    "consulate_code": "IST-DE",
    "priority": "standard",
    "target_submission_date": "2026-03-14",
    "actual_decision_date": "2026-03-28"
  }
}

Request · dry run, every field

curl -X POST 'https://YOUR-CELL.lydira.com/api/v1/imports/visa_cases/validate' \
  -H 'Authorization: Bearer sek_YOUR_API_KEY' \
  -H 'Content-Type: application/json' \
  -H 'Idempotency-Key: 8f14e45f-ea2c-4f33-9a3b-6d0c1b7e5a90' \
  -d '{
  "source_system": "crm",
  "rows": [
    {
      "external_id": "VISA-CASE-1001",
      "destination_country": "DE",
      "visa_class": "schengen_short",
      "passport_country": "TR",
      "priority": "standard",
      "consulate_code": "IST-DE",
      "target_submission_date": "2026-03-14",
      "currency": "EUR",
      "customer_external_id": "CUSTOMER-1001",
      "supplier_external_id": "SUPPLIER-1001",
      "applicant": {
        "first_name": "Ada",
        "last_name": "Lovelace",
        "nationality": "TR",
        "national_id": "10000000146",
        "passport_number": "U01234567",
        "passport_country": "TR",
        "passport_expiry": "2031-06-30",
        "date_of_birth": "1988-04-21",
        "gender": "MRS",
        "frequent_flyer_number": "TK123456789"
      },
      "notes": "Imported from the source system.",
      "transaction_date": "2026-03-14",
      "invoice_date": "2026-03-18",
      "custom_fields": {
        "project_code": "PRJ-2026-001",
        "trip_reason": "meeting"
      },
      "cost_lines": [
        {
          "kind": "government_fee",
          "amount": "1250.00",
          "label": "Imported line",
          "currency": "EUR",
          "vat_rate": "20",
          "tax_inclusive": true,
          "quantity": 1
        }
      ],
      "sell_lines": [
        {
          "kind": "package_price",
          "amount": "1250.00",
          "label": "Imported line",
          "currency": "EUR",
          "vat_rate": "20",
          "tax_inclusive": true,
          "quantity": 1
        }
      ],
      "service_fee": "1250.00",
      "service_fee_vat_rate": "20",
      "service_fee_waived": true
    }
  ]
}'
Response
200
{
  "import": {
    "id": "9d2f7c3a-1b4e-4a7d-8e51-2c0f6b9a4d18",
    "entity": "visa_cases",
    "source_system": "crm",
    "status": "ready",
    "company_id": "9d2f7c3a-1b4e-4a7d-8e51-2c0f6b9a4d18",
    "stats": {
      "total": 1,
      "will_create": 1,
      "will_update": 0,
      "created": 0,
      "updated": 0,
      "skipped": 0,
      "invalid": 0,
      "failed": 0,
      "not_selected": 0
    },
    "error": null,
    "created_at": "2026-03-14T09:30:00Z",
    "links": {
      "self": "/api/v1/imports/9d2f7c3a-1b4e-4a7d-8e51-2c0f6b9a4d18",
      "rows": "/api/v1/imports/9d2f7c3a-1b4e-4a7d-8e51-2c0f6b9a4d18/rows"
    },
    "mode": "validate",
    "async": false,
    "on_conflict": "skip",
    "pricing": "apply_rules"
  },
  "ignored_fields": [],
  "results": [
    {
      "row": 1,
      "external_id": "VISA-CASE-1001",
      "status": "will_create",
      "payer": {
        "outcome": "external_id",
        "customer_id": "9d2f7c3a-1b4e-4a7d-8e51-2c0f6b9a4d18"
      }
    }
  ]
}

The flow

  1. Dry-run. POST …/validate runs the same path and writes nothing.
  2. Send. The same body without /validate. A bad row fails alone and names its field.
  3. Read back. GET /api/v1/imports/{entity}/records/{external_id} answers with your own key.
  4. Listen. Webhooks tell you when the agency changes the record.

Conventions →Webhooks →