Jestiyon - Public API
The public API for companies that integrate with Jestiyon. Use api.jestiyon.com in production and api-sandbox.jestiyon.com for testing.
Base URL
https://api-sandbox.jestiyon.com
Authentication
Every request carries an API key in a header.
API key
Send the API key issued to your company in the X-Api-Key header. Every endpoint except ping and webhook-keys requires a valid key; without one the API returns 401.
curl https://api-sandbox.jestiyon.com/api/v1/companies \-H "X-Api-Key: YOUR_SANDBOX_KEY"
401.GET /api/v1/me to check that a key works and to see which partner, tier and environment it belongs to.403, even with a valid key.Getting started
Form a Turkish company: create it, submit its setup data and documents, then follow its status until formation completes, fixing anything a reviewer rejects along the way.
1. Pick a firm type
Call GET /api/v1/reference/firm-types and note the firmTypeId of the type you need. Its requiresCapital flag tells you which setup fields apply: false for a sole proprietorship (Şahıs), true for a capital company (Limited / Anonim). You send this value as firmTypeId when you create the company.
2. Create the company
POST the company title and its admin user to /api/v1/companies. The response gives you the companyId you use in every later call. The company starts with status new; taxNumber and taxOffice stay null until the tax office is assigned. The e-mail must not already belong to a Jestiyon user, or the request returns 400.
Send an Idempotency-Key header with a unique value of your own, such as a UUID or your order id. If the response never reaches you, repeat the request with the same key: you get the company the first call created instead of email_in_use.
curl -X POST https://api-sandbox.jestiyon.com/api/v1/companies \-H "X-Api-Key: YOUR_SANDBOX_KEY" \-H "Idempotency-Key: 3f1c2b9e-8a4d-4f6b-9c1e-5d7a2b8c4e10" \-H "Content-Type: application/json" \-d '{"title": "Örnek Yazılım Ltd. Şti.","firmTypeId": 2,"firstName": "Ayşe","lastName": "Yılmaz","email": "ayse.yilmaz@ornek.com","mobilePrefix": "+90","mobilePhone": "5321234567"}'
3. Submit setup
POST the formation data to /api/v1/companies/{companyId}/setup in one multipart request: plain form fields for the owner and capital, and lists for partners and managers. The Companies section has the full field list per firm type. The owner's identity front, identity back and address statement are required in this request, and the passport too when the owner is a foreign national (isForeignIdentity is true); the residency document is optional. The status becomes pending_document_review. To replace a single document later, use the documents endpoint with the numeric documentType id from /reference/document-types.
Under Submit setup, select Limited / Anonim to see a complete request for a company with two partners and one manager. Three things are easy to get wrong:
partnerNames twice for two partners. Keep the same order in every partner* field, and likewise in every manager* field: the first value of each describes the first person.partnerRatios are ownership percentages and should add up to 100. Dates are sent as YYYY-MM-DD, and decimals with a dot.managerIdentityFronts and managerIdentityBacks, in the same order as managerNames; a request with one missing is refused. managerResidencies is optional, but if you send it, send one per manager. Partners need no documents; if you have one to add, upload it afterwards with the documents endpoint, passing the partner's detailId from the setup response.setup and to the documents endpoint. Every file in a request is checked before any is stored, so one bad file fails the whole request with 400.4. Poll the status
Poll GET /api/v1/companies/{companyId}/status, or subscribe to the company.status_changed webhook, until status is completed. In the sandbox nothing changes on its own: you move the company yourself, as described in Testing in the sandbox.
curl https://api-sandbox.jestiyon.com/api/v1/companies/1/status \-H "X-Api-Key: YOUR_SANDBOX_KEY"
5. Fix a rejection
If a reviewer rejects part of the submission, status becomes rejected and rejections lists each problem with a code, the detailId of the partner or manager it concerns (null for the owner), the reason and the fix.
The code tells you what to do. These are all the codes:
documentType 1 (IdentityFront) with the documents endpoint, without detailId.documentType 2 (IdentityBack) with the documents endpoint, without detailId.documentType 3 (AddressStatement) with the documents endpoint, without detailId.documentType 4 (Passport) with the documents endpoint, without detailId.documentType 5 (Residency) with the documents endpoint, without detailId.setup again with these fields corrected: name, identityNo, isForeignIdentity, birthDate, birthPlace.setup again with these fields corrected: cityId, townId, address, addressCountry.setup again with these fields corrected: email, phonePrefix, phone.setup again with these fields corrected: totalCapital, shareCount, shareNominalValue, currency.setup again with these fields corrected: activityScope, isVehicleOperation.detailId is set)documentType 1 (IdentityFront) with the documents endpoint, passing the rejection's detailId.detailId is set)documentType 2 (IdentityBack) with the documents endpoint, passing the rejection's detailId.detailId is set)documentType 5 (Residency) with the documents endpoint, passing the rejection's detailId.detailId is set)setup again, correcting that person's value in these partner* or manager* fields: Names, BirthPlaces, BirthDates, NationalIds.detailId is set)setup again, correcting that person's value in these partner* or manager* fields: Emails, PhonePrefixes, Phones, Addresses, AddressCountries.detailId is set)setup again, correcting that person's value in these partner* or manager* fields: Ratios.setup again, send the whole form, not only the corrected fields: every data field is overwritten with what you send. Files are the exception; one you leave out keeps the file already stored. Posting setup again resolves every open data rejection, and a document rejection only if its file is in the request.status returns to pending_document_review. No webhook is sent for that change, so read it from the status endpoint.GET /api/v1/reference/statuses is the single source of truth for the status values the API and webhooks return. Optional fields that have no value yet, such as taxNumber, are returned as null, never omitted.Testing in the sandbox
In the sandbox you move a company through its statuses yourself, so you can test the whole flow without waiting for a reviewer.
In production a Jestiyon reviewer approves or rejects what you submit, and the back office completes the later steps. In the sandbox nothing moves on its own. Two endpoints stand in for those people. They exist only in the sandbox; in production they return 404.
pending_document_review → documents_approved → e_signature_pending → e_invoice_setup → completed.rejected.Run the whole formation
setup as in Getting started. Its status is now pending_document_review.status one step and, if you have a webhook URL registered, sends you a company.status_changed event.e_signature_pending the company is given placeholder taxNumber and taxOffice values, so you can test reading them.curl -X POST https://api-sandbox.jestiyon.com/api/v1/sandbox/companies/1/advance \-H "X-Api-Key: YOUR_SANDBOX_KEY"
Test a rejection
setup, call reject with one or more codes from the table in Getting started. For a code that starts with Detail, also send the detailId of the partner or manager.GET /api/v1/companies/{id}/status now returns rejected with your rejections, and the rejected webhook is sent.setup again.status returns to pending_document_review and you can advance again.curl -X POST https://api-sandbox.jestiyon.com/api/v1/sandbox/companies/1/reject \-H "X-Api-Key: YOUR_SANDBOX_KEY" \-H "Content-Type: application/json" \-d '{"rejections": [{ "code": "OwnerIdentityFront", "reason": "Belge bulanık.", "fix": "Net bir fotoğraf yükleyin." },{ "code": "DetailPersonalInfo", "detailId": 12 }]}'
Receive webhooks while testing
GET /api/v1/webhook-keys on the sandbox address), not the production ones.Before you go live
We give you a live key after your integration has done the following in the sandbox. Every call you make there is recorded with its status and error.code, and this list is checked against that record automatically, so there is nothing to fill in or send us.
GET /api/v1/reference/firm-types.setup for it successfully.GET /api/v1/companies/{id}/status.setup again for the same company.400 from an endpoint, correct the request, and have the same endpoint succeed.Idempotency-Key and get the same company back.completed.2xx.A check stays passed once you pass it. When all nine are done, write to us for your live key; if a call did not behave as you expected, include its error.request_id and we can look it up.
rejected: fix the rejections first. reason and fix are optional, and placeholders are used when you leave them out.Companies
Endpoints in the Companies group.
List companies
Returns every company created under your partner account, with its title, tax details and current formation status.
curl https://api-sandbox.jestiyon.com/api/v1/companies \-H "X-Api-Key: YOUR_SANDBOX_KEY"
[{"companyId": 1,"title": "Örnek Yazılım Ltd. Şti.","taxNumber": null,"taxOffice": null,"status": "pending_document_review"}]
{"error": {"code": "unauthorized","message": "Geçersiz veya eksik API anahtarı.","request_id": "00-6adb6bbc32f4eeed793ff1e8b33db300-7dfeb60b0069031f-01"}}
Create company
Creates a company in Turkey and its admin user under your partner account. The company starts with status new; formation data is sent separately through the setup endpoint.
GET /api/v1/reference/firm-types. It determines which setup fields are required.400.curl -X POST https://api-sandbox.jestiyon.com/api/v1/companies \-H "X-Api-Key: YOUR_SANDBOX_KEY" \-H "Content-Type: application/json" \-d '{"title": "Örnek Yazılım Ltd. Şti.","firmTypeId": 2,"firstName": "Ayşe","lastName": "Yılmaz","email": "ayse.yilmaz@ornek.com","mobilePrefix": "+90","mobilePhone": "5321234567"}'
{"companyId": 1,"title": "Örnek Yazılım Ltd. Şti.","taxNumber": null,"taxOffice": null,"status": "new"}
{"error": {"code": "email_in_use","message": "Bu email adresi ile bir kayıt zaten var.","request_id": "00-6adb6bbc32f4eeed793ff1e8b33db300-7dfeb60b0069031f-01"}}
Get company
Returns one of your companies by its id.
companyId).curl https://api-sandbox.jestiyon.com/api/v1/companies/1 \-H "X-Api-Key: YOUR_SANDBOX_KEY"
{"companyId": 1,"title": "Örnek Yazılım Ltd. Şti.","taxNumber": null,"taxOffice": null,"status": "pending_document_review"}
{"error": {"code": "not_found","message": "Şirket bulunamadı.","request_id": "00-6adb6bbc32f4eeed793ff1e8b33db300-7dfeb60b0069031f-01"}}
Company status
Returns a company's current formation status, whether setup has been submitted, and any open rejections. Each rejection has a code, the detailId of the partner or manager it concerns (null for the owner), the reason and the fix. rejections is always an array; it is empty when nothing is rejected.
companyId).curl https://api-sandbox.jestiyon.com/api/v1/companies/1/status \-H "X-Api-Key: YOUR_SANDBOX_KEY"
{"companyId": 1,"status": "rejected","isSetupSubmitted": true,"rejections": [{"code": "DetailIdentityFront","detailId": 12,"part": "Ortak/Yönetici Kimlik Ön Yüz","reason": "Belge bulanık, okunamıyor.","fix": "Kimliğin ön yüzünün net bir fotoğrafını yeniden yükleyin."},{"code": "OwnerContact","detailId": null,"part": "İletişim Bilgileri","reason": "Telefon numarası eksik haneli.","fix": "Geçerli bir cep telefonu numarası gönderin."}]}
{"error": {"code": "not_found","message": "Şirket bulunamadı.","request_id": "00-6adb6bbc32f4eeed793ff1e8b33db300-7dfeb60b0069031f-01"}}
Submit setup
Submits the formation data and documents in one multipart request: plain form fields for the owner and capital, lists for partners and managers, and the required document files. Use the firm-type switch above the parameters: the required fields and the sample request change with it. The Limited / Anonim sample is a company with two partners and one manager; a sole proprietorship sends only the owner fields. Setup is accepted once. While the company is rejected you may post it again with the whole form: the existing setup is updated, partner and manager ids stay the same, and a file you leave out keeps the one already stored.
companyId).curl -X POST https://api-sandbox.jestiyon.com/api/v1/companies/1/setup \-H "X-Api-Key: YOUR_SANDBOX_KEY" \-F "name=Ayşe Yılmaz" \-F "identityNo=10000000146" \-F "isForeignIdentity=false" \-F "birthDate=1990-05-14" \-F "birthPlace=İstanbul" \-F "cityId=34" \-F "townId=1234" \-F "address=Bağdat Cad. No:1, Kadıköy" \-F "addressCountry=TR" \-F "email=ayse.yilmaz@ornek.com" \-F "phonePrefix=+90" \-F "phone=5321234567" \-F "isVehicleOperation=false" \-F "ownerIdentityFront=@/path/to/ownerIdentityFront" \-F "ownerIdentityBack=@/path/to/ownerIdentityBack" \-F "ownerAddressStatement=@/path/to/ownerAddressStatement" \-F "activityScope=Yazılım geliştirme ve danışmanlık"
{"companyId": 1,"status": "pending_document_review","details": []}
{"error": {"code": "document_missing","message": "ownerAddressStatement: şirket sahibinin adres beyanı zorunludur.","request_id": "00-6adb6bbc32f4eeed793ff1e8b33db300-7dfeb60b0069031f-01"}}
Upload document
Uploads one formation document for the owner, or for a partner or manager. Setup must have been submitted first. The file replaces the one already stored for that document, and if that document was rejected the rejection is resolved. When a company's last open rejection is resolved its status returns to pending_document_review.
companyId).GET /api/v1/reference/document-types (for example 1 = IdentityFront). The owner accepts every type; a partner or manager accepts identity front, identity back and residency.setup response. Leave it out to upload for the owner.curl -X POST https://api-sandbox.jestiyon.com/api/v1/companies/1/documents \-H "X-Api-Key: YOUR_SANDBOX_KEY" \-F "documentType=1" \-F "detailId=12" \-F "file=@/path/to/file"
{"documentId": 5001,"documentType": "IdentityFront","detailId": 12,"fileName": "identity-front.jpg"}
{"error": {"code": "file_too_large","message": "file: dosya boyutu 5 MB sınırını aşıyor.","request_id": "00-6adb6bbc32f4eeed793ff1e8b33db300-7dfeb60b0069031f-01"}}
Me
Endpoints in the Me group.
Current partner
Returns the partner the API key belongs to: its id, name, tier and the environment of the key.
curl https://api-sandbox.jestiyon.com/api/v1/me \-H "X-Api-Key: YOUR_SANDBOX_KEY"
{"partnerId": 1,"name": "Örnek Entegrasyon A.Ş.","tier": "standard","environment": "sandbox"}
{"error": {"code": "unauthorized","message": "Geçersiz veya eksik API anahtarı.","request_id": "00-6adb6bbc32f4eeed793ff1e8b33db300-7dfeb60b0069031f-01"}}
Ping
Endpoints in the Ping group.
Ping
Returns a small response showing the API is up. It needs no API key and is safe to use for uptime checks.
curl https://api-sandbox.jestiyon.com/api/v1/ping
{"status": "ok","version": "1","environment": "sandbox","serverTimeUtc": "2026-08-12T09:30:00Z"}
{"error": {"code": "rate_limit_exceeded","message": "Fazla istek gönderildi. Daha sonra tekrar deneyin.","request_id": "00-6adb6bbc32f4eeed793ff1e8b33db300-7dfeb60b0069031f-01"}}
Reference
Endpoints in the Reference group.
Firm types
Returns the company types that can be formed in Turkey, and whether each requires capital.
curl https://api-sandbox.jestiyon.com/api/v1/reference/firm-types \-H "X-Api-Key: YOUR_SANDBOX_KEY"
[{"firmTypeId": 1,"name": "Şahıs Şirketi","description": "Tek kişilik işletme","countryId": 1,"requiresCapital": false},{"firmTypeId": 2,"name": "Limited Şirket","description": "Sermaye şirketi (Ltd. Şti.)","countryId": 1,"requiresCapital": true}]
{"error": {"code": "unauthorized","message": "Geçersiz veya eksik API anahtarı.","request_id": "00-6adb6bbc32f4eeed793ff1e8b33db300-7dfeb60b0069031f-01"}}
Currencies
Returns the available currencies.
curl https://api-sandbox.jestiyon.com/api/v1/reference/currencies \-H "X-Api-Key: YOUR_SANDBOX_KEY"
[{"id": 1,"name": "TRY"},{"id": 2,"name": "USD"}]
{"error": {"code": "unauthorized","message": "Geçersiz veya eksik API anahtarı.","request_id": "00-6adb6bbc32f4eeed793ff1e8b33db300-7dfeb60b0069031f-01"}}
Cities
Returns the cities (il) of Turkey.
curl https://api-sandbox.jestiyon.com/api/v1/reference/cities \-H "X-Api-Key: YOUR_SANDBOX_KEY"
[{"id": 34,"name": "İstanbul"},{"id": 6,"name": "Ankara"}]
{"error": {"code": "unauthorized","message": "Geçersiz veya eksik API anahtarı.","request_id": "00-6adb6bbc32f4eeed793ff1e8b33db300-7dfeb60b0069031f-01"}}
Towns
Returns the towns (ilçe) of a city.
GET /api/v1/reference/cities.curl https://api-sandbox.jestiyon.com/api/v1/reference/towns?cityId=1 \-H "X-Api-Key: YOUR_SANDBOX_KEY"
[{"id": 1234,"name": "Kadıköy"},{"id": 1235,"name": "Beşiktaş"}]
{"error": {"code": "invalid_request","message": "Geçersiz istek.","request_id": "00-6adb6bbc32f4eeed793ff1e8b33db300-7dfeb60b0069031f-01"}}
Document types
Returns the document types accepted for formation. Send the id as documentType when uploading a document.
curl https://api-sandbox.jestiyon.com/api/v1/reference/document-types \-H "X-Api-Key: YOUR_SANDBOX_KEY"
[{"id": 1,"name": "IdentityFront"},{"id": 2,"name": "IdentityBack"},{"id": 3,"name": "AddressStatement"},{"id": 4,"name": "Passport"},{"id": 5,"name": "Residency"}]
{"error": {"code": "unauthorized","message": "Geçersiz veya eksik API anahtarı.","request_id": "00-6adb6bbc32f4eeed793ff1e8b33db300-7dfeb60b0069031f-01"}}
Statuses
Returns every formation status the API can return. A company starts as new and moves to pending_document_review once setup is submitted.
curl https://api-sandbox.jestiyon.com/api/v1/reference/statuses \-H "X-Api-Key: YOUR_SANDBOX_KEY"
["new","pending_document_review","documents_approved","e_signature_pending","e_invoice_setup","completed","rejected"]
{"error": {"code": "unauthorized","message": "Geçersiz veya eksik API anahtarı.","request_id": "00-6adb6bbc32f4eeed793ff1e8b33db300-7dfeb60b0069031f-01"}}
Sandbox
Endpoints in the Sandbox group.
Advance company (sandbox)
Sandbox only; returns 404 in production. Moves one of your companies to its next formation status, as the reviewer or back office would: pending_document_review → documents_approved → e_signature_pending → e_invoice_setup → completed. It sends the same company.status_changed webhook as production. At e_signature_pending the company is given placeholder taxNumber and taxOffice values. It cannot be used before setup is submitted, while the company is rejected, or after completed.
companyId).curl -X POST https://api-sandbox.jestiyon.com/api/v1/sandbox/companies/1/advance \-H "X-Api-Key: YOUR_SANDBOX_KEY"
{"companyId": 1,"status": "documents_approved","isSetupSubmitted": true,"rejections": []}
{"error": {"code": "invalid_company_status","message": "Şirket reddedilmiş durumda. Önce açık retleri düzeltin; durum yeniden incelemeye döndüğünde ilerletebilirsiniz.","request_id": "00-6adb6bbc32f4eeed793ff1e8b33db300-7dfeb60b0069031f-01"}}
Reject company (sandbox)
Sandbox only; returns 404 in production. Rejects the parts of a company's setup that you name, as a reviewer would. The company becomes rejected, the rejections appear on the status endpoint, and the same company.status_changed webhook as production is sent. Setup must have been submitted first. Calling it again replaces any rejections that are still open.
companyId).code from the rejection code table, a detailId for the codes that start with Detail, and optionally a reason and a fix.curl -X POST https://api-sandbox.jestiyon.com/api/v1/sandbox/companies/1/reject \-H "X-Api-Key: YOUR_SANDBOX_KEY" \-H "Content-Type: application/json" \-d '{"rejections": [{"code": "OwnerIdentityFront","detailId": null,"reason": "Belge bulanık, okunamıyor.","fix": "Kimliğin ön yüzünün net bir fotoğrafını yeniden yükleyin."}]}'
{"companyId": 1,"status": "rejected","isSetupSubmitted": true,"rejections": [{"code": "OwnerIdentityFront","detailId": null,"part": "Kimlik Ön Yüz","reason": "Belge bulanık, okunamıyor.","fix": "Kimliğin ön yüzünün net bir fotoğrafını yeniden yükleyin."}]}
{"error": {"code": "invalid_rejection_code","message": "Geçersiz ret kodu: IdentityFront","request_id": "00-6adb6bbc32f4eeed793ff1e8b33db300-7dfeb60b0069031f-01"}}
Webhook keys
Endpoints in the Webhook keys group.
Webhook keys
Returns the public keys used to verify webhook signatures, as a JWKS. Use the key whose kid matches the one in a delivery's X-Signature header. It needs no API key. Cache the result and fetch again only when you see a kid you do not know.
curl https://api-sandbox.jestiyon.com/api/v1/webhook-keys
{"keys": [{"kty": "EC","crv": "P-256","use": "sig","alg": "ES256","kid": "a1b2c3d4e5","x": "f83OJ3D2xF1Bg8vub9tLe1gHMzV76e8Tus9uZfa4lYs","y": "x_FEzRu9m36HLN_tue659LNpXW6pCyStikYjKIWI5a0"}]}
{"error": {"code": "rate_limit_exceeded","message": "Fazla istek gönderildi. Daha sonra tekrar deneyin.","request_id": "00-6adb6bbc32f4eeed793ff1e8b33db300-7dfeb60b0069031f-01"}}
Webhooks
Jestiyon posts signed events to your webhook URL when a company's formation status changes.
Jestiyon sends a company.status_changed event to your webhook URL each time a company's formation status changes. Each delivery is signed with ES256 (ECDSA P-256); you verify it with Jestiyon's public keys, so no secret is exchanged. Return 2xx within 15 seconds to acknowledge. Anything else is retried with exponential backoff, up to 6 attempts in total.
Verifying deliveries
{t}.{rawBody}, in the form t=<unix>,kid=<key-id>,v1=<base64url>.company.status_changed.t, kid and v1 from X-Signature.kid from GET /api/v1/webhook-keys (JWKS). Cache it, and fetch again only when you see a kid you do not know.v1 over the bytes of {t}.{rawBody}. Use the raw request body, before any JSON parsing or re-serialization.t is too old (for example more than 300 seconds), so a captured request cannot be replayed later.X-Webhook-Id: an event can be delivered more than once.{"keys": [{"kty": "EC","crv": "P-256","use": "sig","alg": "ES256","kid": "a1b2c3d4e5","x": "f83OJ3D2xF1Bg8vub9tLe1gHMzV76e8Tus9uZfa4lYs","y": "x_FEzRu9m36HLN_tue659LNpXW6pCyStikYjKIWI5a0"}]}
import base64, timefrom cryptography.hazmat.primitives.asymmetric import ecfrom cryptography.hazmat.primitives.asymmetric.utils import encode_dss_signaturefrom cryptography.hazmat.primitives import hashesdef b64url(s): return base64.urlsafe_b64decode(s + '=' * (-len(s) % 4))def verify(jwk, headers, raw_body, tolerance=300):p = dict(kv.split('=', 1) for kv in headers['X-Signature'].split(',') if '=' in kv)t, v1 = p['t'], p['v1']x = int.from_bytes(b64url(jwk['x']), 'big')y = int.from_bytes(b64url(jwk['y']), 'big')key = ec.EllipticCurvePublicNumbers(x, y, ec.SECP256R1()).public_key()sig = b64url(v1) # P-1363 r|s -> DERder = encode_dss_signature(int.from_bytes(sig[:32], 'big'),int.from_bytes(sig[32:], 'big'))key.verify(der, f'{t}.'.encode() + raw_body, ec.ECDSA(hashes.SHA256())) # raises if invalidassert abs(time.time() - int(t)) <= tolerance # freshness / replay
Status values
Each company.status_changed event carries one of the status values below. No event is sent when a company enters pending_document_review, whether after setup or after a rejection is fixed.
rejections lists each part with its reason and fix; call the status endpoint for the code and detailId of each.Example payload
rejections is always present. It is an empty array unless status is rejected.
{"event": "company.status_changed","companyId": 1,"status": "documents_approved","rejections": [],"occurredAtUtc": "2026-08-12T09:30:00Z"}
Errors
Every error has the same JSON shape. Use error.code in your code; error.message is for people.
{"error": {"code": "invalid_request","message": "Email gerekli. Şirket adını girin.","request_id": "00-6adb6bbc32f4eeed793ff1e8b33db300-7dfeb60b0069031f-01"}}
error.code is stable: write your error handling against it. error.message is meant for people, is in Turkish, and may change.error.request_id identifies the request. Include it when you contact support.Error codes
A failed validation is invalid_request unless it has one of the more specific codes below. New codes may be added, so treat an unknown code like invalid_request.
setup to have been submitted first.Idempotency-Key was already used to create a different company. Send a new key.Retry-After header.Retrying safely
A timeout or a dropped connection does not tell you whether a request went through. GET requests and document uploads can simply be repeated; a second upload replaces the first. A repeated setup returns setup_already_submitted, which means the first one arrived. Create company is the one call that needs help: send it with an Idempotency-Key header and repeat it with the same key.
title, firmTypeId and email returns 200 with the company already created. Its status is the current one, so it may no longer be new.title, firmTypeId or email returns 409 idempotency_key_reused.Rate limits
When you exceed your rate limit the API returns 429 with a Retry-After header giving the number of seconds to wait. Retrying sooner fails again. Your limit depends on your tier, which GET /api/v1/me returns.
null; it is never left out of the response. Check for null, not for a missing key.Changelog
Notable changes to the public API.
v1.0 — October 2026
setup again while rejected updates the existing setup.code and a detailId, so you can tell what to fix.setup and documents.Idempotency-Key header, so a request whose response was lost can be repeated safely.email_in_use, a missing document is document_missing, and so on. Failures without a specific code are still invalid_request.setup, so the whole flow can be tested without waiting for a reviewer.documentType on upload returns 400 invalid_document_type; it used to be accepted and ignored.t value in X-Signature and occurredAtUtc in the payload are now true UTC. They were three hours ahead, which made a freshness check reject every delivery.v1.0 — August 2026
setup, documents, status), Me, Ping and the Reference lookups.company.status_changed events (ES256) when the formation status changes.firmTypeId; the error envelope's request_id is the one exception.Support
Get help from the Jestiyon integration team.