{"openapi":"3.1.0","info":{"title":"Funciara.com Public API","version":"1.0.0","summary":"Order and track Romanian land registry extracts (extras de carte funciară).","description":"Public HTTP API of [funciara.com](https://funciara.com), a Romanian service that\norders *extrase de carte funciară* (land registry extracts) from ANCPI's ePay portal and delivers the\nofficial, digitally signed PDF by email.\n\nFunciara.com is a private commercial intermediary and is **not** affiliated with ANCPI. The document is\nissued and digitally signed by ANCPI.\n\n## Response envelope\n\nSuccess: `{ \"success\": true, \"data\": ... }`.\nFailure: `{ \"success\": false, \"error\": { \"code\", \"message\", \"details?\", \"requestId?\" } }` — always JSON,\nwith the HTTP status carrying the class of failure and `error.code` the machine-readable reason. Errors\ngenerated at the edge (rate limiting, upstream unavailable) use the same envelope with `details.source`\nset to `edge`. `error.message` is Romanian on application errors because it is rendered to end users.\n\n## Authentication\n\nNo API keys are issued. Read endpoints are open. Order-scoped reads are authorised by the\nper-order `token` (tracking secret) returned when the order is created; an invalid token is\nindistinguishable from a missing order.\n\n`POST /api/search`, `POST /api/checkout` and `POST /api/checkout/reserve` additionally require a\nCloudflare Turnstile token minted by the browser widget on funciara.com, so ordering is documented here\nbut not callable programmatically.\n\n## Rate limits\n\nPer-IP, enforced at the edge and in the application. Exceeding a limit returns `429` with\n`error.code` `RATE_LIMITED`. Per-endpoint limits are noted on each operation.\n\nEvery response carries the remaining quota, so a client can pace itself instead of discovering the\nlimit by being rejected. Two forms are sent: the structured fields from\n[draft-ietf-httpapi-ratelimit-headers](https://datatracker.ietf.org/doc/draft-ietf-httpapi-ratelimit-headers/),\nand the older `X-RateLimit-*` trio for clients that already read it.\n\n```\nRateLimit-Policy: \"default\";q=120;w=60\nRateLimit: \"default\";r=119;t=47\nX-RateLimit-Limit: 120\nX-RateLimit-Remaining: 119\nX-RateLimit-Reset: 47\n```\n\n`q` is the quota per window, `w` the window in seconds, `r` the requests still available and\n`t` the seconds until the window resets. A `429` also carries `Retry-After` in seconds.\n\n## Pricing\n\nA single all-inclusive price of 49 LEI per extract. No tiers, no urgency surcharge.","termsOfService":"https://funciara.com/termeni-si-conditii","contact":{"name":"Funciara.com","url":"https://funciara.com/contact","email":"contact@funciara.com"}},"servers":[{"url":"https://api.funciara.com","description":"Production"}],"externalDocs":{"description":"Plain-language service description for language models","url":"https://funciara.com/llms.txt"},"tags":[{"name":"Nomenclator","description":"County and locality reference data sourced from ANCPI."},{"name":"Search","description":"Look up a land registry record before ordering."},{"name":"Checkout","description":"Create and track an order."},{"name":"Status","description":"Service health and availability."}],"paths":{"/api/counties":{"get":{"tags":["Nomenclator"],"operationId":"listCounties","summary":"List all counties","description":"The 41 Romanian counties plus București. Served from cache; changes ~yearly.","responses":{"200":{"description":"Counties.","content":{"application/json":{"schema":{"type":"object","required":["success","data"],"properties":{"success":{"type":"boolean","const":true},"data":{"type":"array","items":{"type":"object","required":["ancpiId","name"],"properties":{"ancpiId":{"type":"integer","description":"ANCPI county identifier. Use it as `countyId`."},"name":{"type":"string","example":"Brașov"}}}}}}}}},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/ServerError"}}}},"/api/counties/{id}/cities":{"get":{"tags":["Nomenclator"],"operationId":"listCities","summary":"List the localities of a county","parameters":[{"name":"id","in":"path","required":true,"description":"ANCPI county identifier, from `GET /api/counties`.","schema":{"type":"integer"}}],"responses":{"200":{"description":"Localities, ordered by ANCPI identifier.","content":{"application/json":{"schema":{"type":"object","required":["success","data"],"properties":{"success":{"type":"boolean","const":true},"data":{"type":"array","items":{"type":"object","required":["ancpiId","name","countyAncpiId"],"properties":{"ancpiId":{"type":"integer","description":"ANCPI locality identifier. Use it as `cityId`."},"name":{"type":"string","example":"Brașov"},"countyAncpiId":{"type":"integer"}}}}}}}}},"400":{"$ref":"#/components/responses/ValidationError"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/ServerError"}}}},"/api/search":{"post":{"tags":["Search"],"operationId":"searchEstate","summary":"Find a land registry record","description":"Resolves a CF number, cadastral number, topographic number or electronic identifier\nagainst ANCPI and returns the matching immovables. Results are cached for 7 days. The returned\n`searchId` is what links a later checkout to this lookup and is single-use.\n\nRequires a Turnstile token. Rate limit: 30 requests/minute per IP.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SearchEstateInput"}}}},"responses":{"200":{"description":"Matching immovables. An empty `results` array means ANCPI holds no such record.","content":{"application/json":{"schema":{"type":"object","required":["success","data"],"properties":{"success":{"type":"boolean","const":true},"data":{"$ref":"#/components/schemas/SearchEstateResult"}}}}}},"400":{"$ref":"#/components/responses/ValidationError"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/ServerError"},"503":{"$ref":"#/components/responses/AncpiUnavailable"}}}},"/api/checkout":{"post":{"tags":["Checkout"],"operationId":"createOrder","summary":"Create an order and start payment","description":"Validates the record against ANCPI, creates the order and returns a Netopia payment\nURL the customer must be redirected to. Nothing is charged until payment completes on Netopia's page.\n\nRequires a Turnstile token and a `searchId` from a prior `POST /api/search`.\nRate limit: 10 requests/hour per IP.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CheckoutInput"}}}},"responses":{"200":{"description":"Order created. Redirect the customer to `paymentUrl`; keep `trackingToken` to read status later.","content":{"application/json":{"schema":{"type":"object","required":["success","data"],"properties":{"success":{"type":"boolean","const":true},"data":{"$ref":"#/components/schemas/CheckoutResult"}}}}}},"400":{"$ref":"#/components/responses/ValidationError"},"409":{"$ref":"#/components/responses/DuplicateOrder"},"422":{"$ref":"#/components/responses/NotElectronic"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/ServerError"},"503":{"$ref":"#/components/responses/AncpiUnavailable"}}}},"/api/checkout/reserve":{"post":{"tags":["Checkout"],"operationId":"reserveOrder","summary":"Reserve an order while ANCPI is unreachable","description":"Captures a complete order without payment and without contacting ANCPI. Once ANCPI is\nreachable again the record is validated and a payment link is emailed. A reservation costs nothing and\ndoes not guarantee that the document can be issued.\n\nRequires a Turnstile token. Rate limit: 10 requests/hour per IP.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ReserveInput"}}}},"responses":{"200":{"description":"Reservation recorded.","content":{"application/json":{"schema":{"type":"object","required":["success","data"],"properties":{"success":{"type":"boolean","const":true},"data":{"$ref":"#/components/schemas/ReserveResult"}}}}}},"400":{"$ref":"#/components/responses/ValidationError"},"409":{"$ref":"#/components/responses/DuplicateOrder"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/ServerError"}}}},"/api/checkout/orders/{id}":{"get":{"tags":["Checkout"],"operationId":"getOrderStatus","summary":"Read the status of an order","description":"Poll while the order is in flight. `pdfUrl` becomes a presigned download link once\n`status` is `COMPLETED`. Rate limit: 60 requests/minute per IP.","security":[{"trackingToken":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"Order number or internal order id, as returned at creation.","schema":{"type":"string"}},{"name":"token","in":"query","required":true,"description":"Tracking secret returned when the order was created.","schema":{"type":"string"}}],"responses":{"200":{"description":"Current order state.","content":{"application/json":{"schema":{"type":"object","required":["success","data"],"properties":{"success":{"type":"boolean","const":true},"data":{"$ref":"#/components/schemas/OrderStatus"}}}}}},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/ServerError"}}}},"/api/checkout/orders/{id}/resume-payment":{"get":{"tags":["Checkout"],"operationId":"resumePayment","summary":"Resume payment for an unpaid order","description":"Mints a fresh Netopia session and redirects to it. Orders that are no longer\n`PENDING` redirect to the tracking page instead. Intended for a link in a reminder email — it\nredirects rather than returning JSON. Rate limit: 10 requests/minute per IP.","security":[{"trackingToken":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}},{"name":"token","in":"query","required":true,"schema":{"type":"string"}}],"responses":{"302":{"description":"Redirect to the payment page, or to order tracking when payment no longer applies.","headers":{"Location":{"schema":{"type":"string","format":"uri"}}}},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"},"502":{"description":"The payment processor rejected the request. Returns an HTML page, not JSON.","content":{"text/html":{"schema":{"type":"string"}}}}}}},"/api/site-status":{"get":{"tags":["Status"],"operationId":"getSiteStatus","summary":"Whether an ANCPI outage is being announced","description":"`outage: true` means ANCPI's online systems are unreachable, so extracts cannot be\ndelivered and only reservations are accepted. Presentation state — it does not gate the order pipeline.","responses":{"200":{"description":"Current announcement state.","content":{"application/json":{"schema":{"type":"object","required":["success","data"],"properties":{"success":{"type":"boolean","const":true},"data":{"$ref":"#/components/schemas/SiteStatus"}}}}}},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/ServerError"}}}},"/api/stats/public":{"get":{"tags":["Status"],"operationId":"getPublicStats","summary":"Aggregate delivery statistics","description":"Aggregates only — no personal data. Cached for one hour.","responses":{"200":{"description":"Aggregate statistics.","content":{"application/json":{"schema":{"type":"object","required":["success","data"],"properties":{"success":{"type":"boolean","const":true},"data":{"$ref":"#/components/schemas/PublicStats"}}}}}},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/ServerError"}}}},"/api/health":{"get":{"tags":["Status"],"operationId":"getHealth","summary":"Liveness of the API and its datastores","responses":{"200":{"description":"Health report. `status` is `degraded` when a dependency is down.","content":{"application/json":{"schema":{"type":"object","required":["status","timestamp","services"],"properties":{"status":{"type":"string","enum":["ok","degraded"]},"timestamp":{"type":"string","format":"date-time"},"services":{"type":"object","required":["database","redis"],"properties":{"database":{"type":"string","enum":["up","down"]},"redis":{"type":"string","enum":["up","down"]}}}}}}}}}}},"/api/openapi.json":{"get":{"tags":["Status"],"operationId":"getOpenApiDocument","summary":"This document","description":"Also published at https://funciara.com/openapi.json.","responses":{"200":{"description":"The OpenAPI document.","content":{"application/json":{"schema":{"type":"object"}}}}}}}},"components":{"securitySchemes":{"trackingToken":{"type":"apiKey","in":"query","name":"token","description":"Per-order tracking secret issued at order creation. Grants read access to that one order."}},"headers":{"RateLimit":{"description":"Remaining quota for the applicable policy, as an RFC 9651 structured field: `r` requests left, `t` seconds until the window resets.","schema":{"type":"string","example":"\"default\";r=119;t=47"}},"RateLimitPolicy":{"description":"The quota policy in force: `q` requests per `w` seconds. Constant per endpoint.","schema":{"type":"string","example":"\"default\";q=120;w=60"}},"RetryAfter":{"description":"Seconds to wait before retrying. Sent on 429.","schema":{"type":"integer","example":60}}},"schemas":{"ApiError":{"type":"object","properties":{"success":{"type":"boolean","const":false},"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{},"requestId":{"type":"string"}},"required":["code","message"],"additionalProperties":false}},"required":["success","error"],"additionalProperties":false},"SearchEstateInput":{"type":"object","properties":{"estateIdentifier":{"type":"string","minLength":1,"maxLength":100},"countyId":{"type":"integer","exclusiveMinimum":0,"maximum":9007199254740991},"cityId":{"type":"integer","exclusiveMinimum":0,"maximum":9007199254740991},"turnstileToken":{"type":"string","minLength":1,"maxLength":4096}},"required":["estateIdentifier","countyId","cityId","turnstileToken"]},"SearchEstateResult":{"type":"object","properties":{"searchId":{"type":"string","minLength":1},"results":{"type":"array","items":{"type":"object","properties":{"electronicIdentifier":{"type":"string"},"address":{"type":"string"},"immovableTypeCode":{"anyOf":[{"type":"string"},{"type":"null"}]},"immovableTypeLabel":{"type":"string"},"legalAreaSqm":{"anyOf":[{"type":"number"},{"type":"null"}]},"measuredAreaSqm":{"anyOf":[{"type":"number"},{"type":"null"}]},"topographicNo":{"anyOf":[{"type":"string"},{"type":"null"}]},"previousLandBookNo":{"anyOf":[{"type":"string"},{"type":"null"}]},"previousCadNo":{"anyOf":[{"type":"string"},{"type":"null"}]},"status":{"anyOf":[{"type":"string"},{"type":"null"}]},"landBookTypeCode":{"anyOf":[{"type":"string"},{"type":"null"}]}},"required":["electronicIdentifier","address","immovableTypeCode","immovableTypeLabel","legalAreaSqm","measuredAreaSqm","topographicNo","previousLandBookNo","previousCadNo","status","landBookTypeCode"],"additionalProperties":false}}},"required":["searchId","results"],"additionalProperties":false},"CheckoutInput":{"type":"object","properties":{"estateIdentifier":{"type":"string","minLength":1,"maxLength":100},"countyId":{"type":"integer","exclusiveMinimum":0,"maximum":9007199254740991},"countyName":{"type":"string","minLength":1},"cityId":{"type":"integer","exclusiveMinimum":0,"maximum":9007199254740991},"cityName":{"type":"string","minLength":1},"invoiceCountry":{"type":"string","minLength":1,"maxLength":100},"invoiceCounty":{"type":"string","minLength":1,"maxLength":100},"invoiceCity":{"type":"string","maxLength":100},"invoiceAddress":{"type":"string","maxLength":255},"phone":{"type":"string","pattern":"^(\\+40|0)[0-9]{9}$"},"companyName":{"type":"string"},"companyCui":{"type":"string","pattern":"^(RO)?[0-9]{2}[0-9]{6}$"},"name":{"type":"string","minLength":2},"email":{"type":"string","format":"email","pattern":"^(?!\\.)(?!.*\\.\\.)([A-Za-z0-9_'+\\-\\.]*)[A-Za-z0-9_+-]@([A-Za-z0-9][A-Za-z0-9\\-]*\\.)+[A-Za-z]{2,}$"},"searchId":{"type":"string","minLength":1,"maxLength":64},"turnstileToken":{"type":"string","minLength":1,"maxLength":4096},"landingPath":{"type":"string","maxLength":256,"pattern":"^\\/(?!\\/)[^?#\\s]*$"}},"required":["estateIdentifier","countyId","countyName","cityId","cityName","invoiceCountry","invoiceCounty","name","email","searchId"]},"ReserveInput":{"type":"object","properties":{"estateIdentifier":{"type":"string","minLength":1,"maxLength":100},"countyId":{"type":"integer","exclusiveMinimum":0,"maximum":9007199254740991},"countyName":{"type":"string","minLength":1},"cityId":{"type":"integer","exclusiveMinimum":0,"maximum":9007199254740991},"cityName":{"type":"string","minLength":1},"invoiceCountry":{"type":"string","minLength":1,"maxLength":100},"invoiceCounty":{"type":"string","minLength":1,"maxLength":100},"invoiceCity":{"type":"string","maxLength":100},"invoiceAddress":{"type":"string","maxLength":255},"phone":{"type":"string","pattern":"^(\\+40|0)[0-9]{9}$"},"companyName":{"type":"string"},"companyCui":{"type":"string","pattern":"^(RO)?[0-9]{2}[0-9]{6}$"},"name":{"type":"string","minLength":2},"email":{"type":"string","format":"email","pattern":"^(?!\\.)(?!.*\\.\\.)([A-Za-z0-9_'+\\-\\.]*)[A-Za-z0-9_+-]@([A-Za-z0-9][A-Za-z0-9\\-]*\\.)+[A-Za-z]{2,}$"},"searchId":{"type":"string","minLength":1,"maxLength":64},"turnstileToken":{"type":"string","minLength":1,"maxLength":4096},"landingPath":{"type":"string","maxLength":256,"pattern":"^\\/(?!\\/)[^?#\\s]*$"}},"required":["estateIdentifier","countyId","countyName","cityId","cityName","invoiceCountry","invoiceCounty","name","email"]},"SiteStatus":{"type":"object","properties":{"outage":{"type":"boolean"}},"required":["outage"],"additionalProperties":false},"PublicStats":{"type":"object","properties":{"ordersCompleted":{"type":"integer","minimum":0,"maximum":9007199254740991},"lastOrderNumber":{"anyOf":[{"type":"integer","minimum":0,"maximum":9007199254740991},{"type":"null"}]},"p50DeliveryMinutes":{"anyOf":[{"type":"number","minimum":0},{"type":"null"}]},"lastDeliverySeconds":{"anyOf":[{"type":"integer","minimum":0,"maximum":9007199254740991},{"type":"null"}]},"lastDeliveryCity":{"anyOf":[{"type":"string"},{"type":"null"}]},"lastDeliveryAt":{"anyOf":[{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"},{"type":"null"}]},"generatedAt":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"}},"required":["ordersCompleted","lastOrderNumber","p50DeliveryMinutes","lastDeliverySeconds","lastDeliveryCity","lastDeliveryAt","generatedAt"],"additionalProperties":false},"OrderStatus":{"type":"object","required":["id","orderNumber","orderLabel","invoiceLabel","status","createdAt","countyName","cityName","estateIdentifier","pdfUrl","invoicePdfUrl","reservedDuringOutage","reservationOutcome"],"properties":{"id":{"type":"string"},"orderNumber":{"type":"integer"},"orderLabel":{"type":"string","example":"FC-10071"},"invoiceLabel":{"type":["string","null"],"description":"SmartBill series + number, once issued."},"status":{"type":"string","enum":["RESERVED","PENDING","PAID","PROCESSING","AWAITING_DOCUMENT","COMPLETED","FAILED","CANCELLED"]},"createdAt":{"type":"string","format":"date-time"},"countyName":{"type":"string"},"cityName":{"type":"string"},"estateIdentifier":{"type":"string"},"pdfUrl":{"type":["string","null"],"format":"uri","description":"Short-lived presigned link to the ANCPI-signed extract. Non-null only once `status` is `COMPLETED`."},"invoicePdfUrl":{"type":["string","null"],"format":"uri"},"reservedDuringOutage":{"type":"boolean","description":"True when the order entered through the reservation lane during an ANCPI outage."},"reservationOutcome":{"oneOf":[{"type":"string","enum":["PROMOTED","NOT_FOUND","NOT_ELECTRONIC","EXPIRED","CANCELLED_BY_OPERATOR","DUPLICATE_HELD"]},{"type":"null"}],"description":"Terminal disposition of a reservation; null until the drain worker resolves it."}}},"CheckoutResult":{"type":"object","required":["orderId","orderNumber","orderLabel","paymentUrl","trackingToken"],"properties":{"orderId":{"type":"string"},"orderNumber":{"type":"integer"},"orderLabel":{"type":"string","example":"FC-10071"},"paymentUrl":{"type":"string","format":"uri","description":"Netopia hosted payment page."},"trackingToken":{"type":"string","description":"Pass as `token` to read the order later."}}},"ReserveResult":{"type":"object","required":["orderId","orderNumber","orderLabel","trackingToken"],"properties":{"orderId":{"type":"string"},"orderNumber":{"type":"integer"},"orderLabel":{"type":"string","example":"FC-10071"},"trackingToken":{"type":"string"}}}},"responses":{"ValidationError":{"description":"The request body failed validation. `error.code` is `VALIDATION_ERROR` and `error.details` lists the offending fields.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"NotFound":{"description":"No such order, or the tracking token does not match it. The two are deliberately indistinguishable.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"DuplicateOrder":{"description":"A live order already exists for this property and customer. `error.code` is `DUPLICATE_ORDER`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"NotElectronic":{"description":"ANCPI does not hold this record in electronic format, so it cannot be issued automatically. `error.code` is `ANCPI_NO_ELECTRONIC_FORMAT`. Nothing was charged.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"RateLimited":{"description":"Too many requests. `error.code` is `RATE_LIMITED`.","headers":{"Retry-After":{"$ref":"#/components/headers/RetryAfter"},"RateLimit":{"$ref":"#/components/headers/RateLimit"},"RateLimit-Policy":{"$ref":"#/components/headers/RateLimitPolicy"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"AncpiUnavailable":{"description":"ANCPI could not be reached or is busy. When `error.details.canReserve` is true the order can be placed through `POST /api/checkout/reserve` instead.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"ServerError":{"description":"Unexpected server error. `error.requestId` identifies the request in our logs.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}}}