Facility Verify API

Integration documentation for verifying ministry facility documents and reporting publication status.

v1.0

Overview

This API lets platforms look up publishable ministry documents (TTBİS, simple accommodation, daily rental) and report whether related listings are published. All endpoints below require HTTP Basic Authentication, except the health endpoints, which are public.

Only publishable documents are returned. Non-publishable documents are excluded from the response entirely.

Contents

Authentication

Every request must include an Authorization header with HTTP Basic credentials. Use the platform username and password provided by the platform administrator.

Authorization: Basic base64(username:password)
Do not share credentials with third parties or expose them in client-side code.

Document types

The document-list response always includes three arrays. Each array has its own schema — field sets are not the same across document types.

Response field documentType Description
ttbisDocuments ttbis Tourism Operation Certificates (TTBİS)
simpleAccommodationDocuments simpleAccommodation Simple Accommodation certificates
dailyRentalDocuments dailyRental Daily Rental (short-term housing) certificates

Shared fields (all types)

traceId, documentType, documentNo, documentDate, documentOwner, city, district

Type-specific fields

Array Additional fields
ttbisDocuments documentStatus, facilityName, facilityType, facilitySubType, facilityClass, documentOwnerNationalIds, documentOwnerTaxNos
simpleAccommodationDocuments documentPersonType, taxNo, yetkiliTcKimlikNo, basvuruKimlikNo, isletmeKimlikNo, title, facilityName, facilityType
dailyRentalDocuments companyTitle, documentPersonType, taxNo, documentOwnerNationalIds, neighbourhood

documentOwnerNationalIds / documentOwnerTaxNos are arrays and may contain multiple values for multi-partner facilities.

Typical flow

  1. Call GET /document-list with the owner's tax/national ID and/or document number (and supplierId when searching by docNo only).
  2. Pick the relevant document and save its traceId.
  3. When the listing goes live or is removed, call POST /publication-notifications with that traceId (send it again with the same traceId and documentNo whenever the status changes, or with a freshly issued traceId from a new document list call).
  4. On onboarding, every platform must send a bulk publication notification for all of its currently live domestic listings, and keep them up to date afterwards (new publications and removals).

1. Document list

GET/document-list

Returns verified ministry documents. At least one of identifyNo or docNo is required. When both are provided, results must match both. When searching by docNo only (without identifyNo), supplierId is required; requests with only docNo are rejected.

Only publishable documents are returned. Non-publishable documents are excluded from the response entirely.

Query parameters

Parameter Type Required Description
identifyNo string Conditional Digits only. 10 digits = tax number (VKN), 11 digits = national ID (TCKN). Leading zeros are significant. Required if docNo is not provided.
docNo string Conditional Document number (belgeNo). Required if identifyNo is not provided.
supplierId integer Conditional Id of the supplier (B2B provider) from which the platform sources the listing. Obtained from GET /options/platforms (use the value field). Required only when searching by docNo without identifyNo. Not needed when identifyNo is provided. Must be an approved platform id.

Why supplierId?

Listings supplied by B2B providers often come without a tax/national ID. In that case the platform verifies the facility by document number alone, and supplierId records which supplier the listing was sourced from — keeping identity-less document searches traceable.

Success response

200

Response shapes differ by document type. See Document types for shared vs type-specific fields. Unused arrays are returned as empty lists.
{
  "apiVersion": "1.0",
  "identifyNo": "1234567890",
  "docNo": null,
  "count": 3,
  "ttbisDocuments": [
    {
      "traceId": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
      "documentType": "ttbis",
      "documentNo": "03-284",
      "documentDate": "2022-01-15",
      "documentStatus": "Belgeli Tesisler",
      "documentOwner": "Example Owner",
      "facilityName": "Example Facility",
      "facilityType": "...",
      "facilitySubType": "...",
      "facilityClass": "...",
      "documentOwnerNationalIds": ["11111111111"],
      "documentOwnerTaxNos": ["1234567890"],
      "city": "İstanbul",
      "district": "..."
    }
  ],
  "simpleAccommodationDocuments": [
    {
      "traceId": "7c9e6679-7425-40de-944b-e07fc1f90ae7",
      "documentType": "simpleAccommodation",
      "documentNo": "SA-1001",
      "documentDate": "2023-05-20",
      "documentOwner": "Example Owner",
      "documentPersonType": "...",
      "taxNo": "1234567890",
      "yetkiliTcKimlikNo": "11111111111",
      "basvuruKimlikNo": "...",
      "isletmeKimlikNo": "...",
      "title": "...",
      "facilityName": "Example Facility",
      "facilityType": "...",
      "city": "Ankara",
      "district": "..."
    }
  ],
  "dailyRentalDocuments": [
    {
      "traceId": "9b1deb4d-3b7d-4bad-9bdd-2b0d7b3dcb6d",
      "documentType": "dailyRental",
      "documentNo": "DR-2002",
      "documentDate": "2024-02-10",
      "documentOwner": "Example Owner",
      "companyTitle": "...",
      "documentPersonType": "...",
      "taxNo": "1234567890",
      "documentOwnerNationalIds": ["11111111111"],
      "city": "İzmir",
      "district": "...",
      "neighbourhood": "..."
    }
  ]
}

Each document contains a traceId — store it for publication notifications. A traceId is issued per request: every call returns a new traceId for each document, and each one stays valid for publication notifications.

Examples

Search by identifyNo:

curl -u 'USERNAME:PASSWORD' \
  'https://YOUR_HOST/document-list?identifyNo=1234567890'

Search by docNo (supplierId required):

curl -u 'USERNAME:PASSWORD' \
  'https://YOUR_HOST/document-list?docNo=03-284&supplierId=12'

2. Publication notifications

POST/publication-notifications

Reports whether the listings of a previously verified document are published. Send the traceId received from the document list response. A document may have several listings, so publication state is reported as an array.

On onboarding, every platform must send a bulk publication notification for all of its currently live domestic listings, and keep them up to date afterwards (new publications and removals).
The document behind the traceId must still be publishable: its document number is looked up in the ministry documents of the trace's document type. If it is not there (revoked, expired or no longer published by the ministry), the request is rejected with 409.
If the ministry has cancelled the document since the document list was fetched, only removals are accepted: a request whose listings are all isPublished: false is recorded as usual, while a request containing any isPublished: true listing is rejected with 409. When the ministry data holds a cancellation date or a document status for the cancelled document, the error message repeats them.
Only state changes are recorded. For every listing the last notification your platform recorded for the same documentNo and listingUrl must carry the opposite isPublished value, so a listing alternates true ↔ false. A listing that repeats the state it is already in is skipped: no new notification is written for it and its entry in the response carries a warning naming the date of that existing state. Its createdAtUtc is that same date, not the time of the call.
The check runs per listing, not per request. The request is never rejected because of it: the remaining listings are recorded as usual and the response stays 201. Sending two listings where one repeats its state and the other changes it yields one warned entry and one recorded entry. The rule applies inside a single request too, so the same listingUrl carrying the same state twice has its second entry skipped. Removals sent without a listingUrl share one state per document, so two consecutive URL-less isPublished: false notifications produce the same warning.
A listing whose first notification is isPublished: false is recorded, but its entry in the response carries a warning because no earlier publication notification exists for it. Listings with nothing to warn about omit the field.

Headers

Header Value Required
Authorization Basic credentials Yes
Content-Type application/json Yes

Request body

{
  "traceId": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
  "documentNo": "03-284",
  "listings": [
    {
      "listingUrl": "https://example.com/listing/123",
      "isPublished": true
    },
    {
      "listingUrl": "https://example.com/listing/456",
      "isPublished": false
    }
  ]
}
Field Type Required Description
traceId string (UUID) Yes Trace id from document-list response.
documentNo string Yes Must match the document associated with the traceId.
listings array Yes Listings of the document. Must contain at least one item.
listings[].isPublished boolean Yes true when the listing is live; false when removed/unpublished.
listings[].listingUrl string Conditional Absolute URL. Required when isPublished is true. Optional when isPublished is false.

Success responses

201 Created — a notification is recorded for each listing in the request. Every call records new notifications; the latest ones for a listing reflect the current state.

{
  "traceId": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
  "documentNo": "03-284",
  "listings": [
    {
      "listingUrl": "https://example.com/listing/123",
      "isPublished": true,
      "createdAtUtc": "2026-07-30T12:00:00Z"
    },
    {
      "listingUrl": "https://example.com/listing/456",
      "isPublished": false,
      "createdAtUtc": "2026-07-30T12:00:00Z",
      "warning": "https://example.com/listing/456 is reported as isPublished false but this is its first notification; no earlier publication notification exists for it."
    }
  ]
}

When a listing repeats the state it is already in, the request still succeeds with 201; only that entry is skipped and warned, while the others are recorded:

{
  "traceId": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
  "documentNo": "03-284",
  "listings": [
    {
      "listingUrl": "https://example.com/listing/123",
      "isPublished": true,
      "createdAtUtc": "2026-07-28T09:15:00Z",
      "warning": "https://example.com/listing/123 is already isPublished true as of 2026-07-28T09:15:00Z; only state changes are recorded, so no new notification was written for it."
    },
    {
      "listingUrl": "https://example.com/listing/456",
      "isPublished": true,
      "createdAtUtc": "2026-07-30T12:00:00Z"
    }
  ]
}

Example

curl -u 'USERNAME:PASSWORD' \
  -X POST 'https://YOUR_HOST/publication-notifications' \
  -H 'Content-Type: application/json' \
  -d '{
    "traceId": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
    "documentNo": "03-284",
    "listings": [
      { "listingUrl": "https://example.com/listing/123", "isPublished": true }
    ]
  }'

3. Platform options

GET/options/platforms

Returns approved selectable platforms as { label, value }[]. Use value as supplierId when searching by docNo without identifyNo.

Headers

Header Value Required
Authorization Basic credentials Yes

Success response

200

[
  {
    "label": "Acme Supplier",
    "value": "12"
  }
]

Example

curl -u 'USERNAME:PASSWORD' \
  'https://YOUR_HOST/options/platforms'

4. Create platform

POST/platforms

Creates a platform by name. New platforms start with isApproved=false. After admin approval they appear in GET /options/platforms and can be used as supplierId.

Headers

Header Value Required
Authorization Basic credentials Yes
Content-Type application/json Yes

Request body

{
  "name": "Acme Supplier"
}
Field Type Required Description
name string Yes Platform display name (max 200 characters). Must be unique.

Success response

201

{
  "id": 12,
  "name": "Acme Supplier",
  "isApproved": false
}

Example

curl -u 'USERNAME:PASSWORD' \
  -X POST 'https://YOUR_HOST/platforms' \
  -H 'Content-Type: application/json' \
  -d '{"name":"Acme Supplier"}'

5. Health check

GET/health

Liveness endpoint for uptime checks and orchestrators. This is the only endpoint that does not require authentication, and it never contains facility or platform data.

It only reports that the API process is alive and serving requests. No dependency is contacted, so the result is always 200.

Response

200 GET /health

{
  "status": "Healthy",
  "version": "1.0",
  "timestamp": "2026-01-01T00:00:00.0000000+00:00"
}
Field Type Description
status string Always Healthy when the process answers
version string API version serving the request
timestamp string UTC time the response was produced (ISO 8601)
Point liveness probes at /health. A dependency outage must not restart the process, so /health deliberately stays 200 in that case.

Example

curl 'https://YOUR_HOST/health'

Rate limiting

Your platform may be given a request quota. The quota is counted per endpoint and is shared across all API instances, so it does not matter which server answers your call.

The window opens on your first request to that endpoint and lasts 15 minutes. It is never extended by further requests: if you open the window at 10:14 it resets at 10:29, even if you spend the whole quota at 10:25.

Header When Meaning
X-RateLimit-Limit every rate-limited response Requests allowed per window
X-RateLimit-Remaining every rate-limited response Requests left in the current window
X-RateLimit-Reset every rate-limited response Unix timestamp (seconds, UTC) at which the window resets
Retry-After 429 only Seconds to wait before retrying

Once the quota is spent the API answers 429 with the standard error body. Read X-RateLimit-Remaining to pace yourself instead of waiting for the rejection.

Platforms without a configured quota are not limited and receive no X-RateLimit-* headers. GET /health is never rate limited.

Example

HTTP/1.1 429 Too Many Requests
Retry-After: 240
X-RateLimit-Limit: 600
X-RateLimit-Remaining: 0
X-RateLimit-Reset: 1755437340

{
  "error": "Rate limit exceeded. Retry after 240 seconds."
}

Error responses

All error responses use this JSON structure:

{
  "error": "Human-readable error message"
}
Status When
400 Invalid or missing query/body parameters (wrong types, identifyNo length or non-digit characters, docNo without supplierId, invalid supplierId, publication body validation)
401 Missing or invalid Basic Authentication credentials, or platform not approved / credentials not yet delivered
404 traceId not found (POST /publication-notifications)
409 Platform name already exists (POST /platforms), or the document behind the traceId is missing from the ministry documents, or it is cancelled and the request publishes a listing (POST /publication-notifications)
429 The platform spent its request quota for the current window; retry after the number of seconds in Retry-After (see Rate limiting)
500 Unexpected server-side error
504 The request exceeded the server request timeout

Example errors

{
  "error": "supplierId is required when searching by docNo without identifyNo"
}
{
  "error": "identifyNo must be between 10 and 11 characters long"
}
{
  "error": "identifyNo must contain digits only"
}
{
  "error": "traceId must be a valid UUID"
}
{
  "error": "listings[1].listingUrl is required when isPublished is true."
}
{
  "error": "documentNo '03-284' is not publishable; it is not present in the verified 'ttbis' documents."
}
{
  "error": "documentNo '03-284' is cancelled in the verified 'ttbis' documents and can no longer be published. Cancellation date: 2024-11-03 00:00:00+00. Only listings with isPublished false are accepted for it."
}
{
  "error": "The request timed out."
}