# FirmAPI – API Documentation

Base URL: `https://firmapi.sk/v1`

FirmAPI provides a REST API for Slovak company data and extended enrichments including tax data, bank accounts, financial metrics, financial statements, insolvency, public procurement, and notifications via webhook or email.

## Authentication

All API requests require authentication using an API key. You can create API keys in your [dashboard](https://api.firmapi.sk/en/dashboard/api-keys). A free API key is available after signing up: https://api.firmapi.sk/en/register

Include your API key in the `Authorization` header:

```bash
curl -H "Authorization: Bearer fa_your_key" "https://firmapi.sk/v1/company/ico/51636549"
```

Or use the `X-API-Key` header:

```bash
curl -H "X-API-Key: fa_your_key" "https://firmapi.sk/v1/company/ico/51636549"
```

## Sandbox

Test the API without registration. The sandbox provides 10 demo companies with full data, no rate limits, and unlimited requests. Perfect for development and integration testing.

Sandbox API Key: `fa_sandbox_test_key_firmapi_sk_2026`

Available sandbox endpoints:

| Endpoint | Description | Auth |
|---|---|---|
| `GET /v1/sandbox/companies` | List all demo companies | Not required |
| `GET /v1/sandbox/company/ico/{ico}` | Full company profile with all scopes | Not required |
| `GET /v1/sandbox/search/autocomplete?q=` | Autocomplete search in demo companies | Sandbox key |

SDK Examples: PHP `\FirmApi\Client::sandbox()`, JavaScript/TypeScript `FirmApi.sandbox()`.

## Data Sources

FirmAPI combines core registers and supplemental enrichment sources into a single response:

| Source | Data | Update Frequency |
|---|---|---|
| RPO (Register of Legal Entities) | Name, registration number, address, legal form, status | Daily |
| ORSR (Commercial Register) | Shareholders, statutory bodies, business activities, registered capital | On demand / monthly |
| Tax and financial sources | Tax ID, VAT ID, VAT payer status, core financial metrics, financial statements | Continuously based on source availability |
| EU VIES | VAT ID validation | On demand (7-day cache) |
| Public registers and bulletins | Debtor lists, commercial bulletin, insolvency, enforcement authorizations, public contracts and procurement, Public Procurement Office registers, Tax Authority registers, sanctions lists (EU/OFAC) and other public registers | Continuously based on source availability |

## Stale Data & Background Refresh

FirmAPI returns the latest available data immediately and aims to keep data fetched from sources newer than **10 days**. If ORSR data or supplemental source data is older, it automatically triggers a background refresh.

If ORSR details are older than **30 days** or data fetched from supplemental sources is older than **10 days**, the response will include staleness markers:

- `meta.stale: true` – data is outdated
- `meta.retry_at` – ISO 8601 timestamp when to poll again for fresh data
- `meta.stale_reason` – for example `orsr_data_outdated`, `enrichment_data_outdated`, or `orsr_and_enrichment_data_stale`

Retry the same request after `retry_at`. Background refresh typically completes within 10-15 seconds.

```json
{
  "data": { "...": "..." },
  "meta": {
    "rpo_updated_at": "2026-02-05T06:00:00Z",
    "orsr_synced_at": "2025-12-01T14:30:00Z",
    "source": "database",
    "stale": true,
    "retry_at": "2026-02-05T12:00:15Z",
    "stale_reason": "orsr_data_outdated"
  }
}
```

## Endpoints

Public endpoints (no authentication): GET /status (service status) and GET /health (monitoring health-check).

Account calls (`/account/*`), notification management (`/notifications/*`) and batch status/results polling cost no credits and keep working when the balance is exhausted.

### Get Company by IČO

| Endpoint | Description |
|---|---|
| `GET /company/ico/{ico}` | Get company details by IČO (registration number). 8-digit company registration number |
| `GET /company/id/{orsrId}` | Get company details by ORSR internal ID. |
| `GET /company/{id}` | Get company details by internal database ID. |

`scope` – Comma-separated list of enrichment scopes to include. Omit for base data only (fastest). By default, company endpoints return only core data (name, address, status). Use the scope parameter to include additional enrichment data. Each scope requires the corresponding feature on your plan. `scope=all`.

**Available scopes:** `tax, bank_accounts, contacts, financials, debtor_status, financial_statements, insolvency, execution_authorizations, rpvs, nbs, tax_reliability, erased_vat, commercial_bulletin, public_contracts, procurement, trade_license_activities, reges, contracting_authority, debarred, uvo_references, crp_projects, ted_tenders, gleif, sanctions, social_enterprise, replik_administrator, sbs, transport_licence, utility_licence, fs_imports, illegal_employment, court_decisions, employer_headcount, soi_travel_agency, svps_establishments`

```bash
curl -H "Authorization: Bearer fa_your_key" \
  "https://firmapi.sk/v1/company/ico/51636549?scope=tax,financials,debtor_status"
```

```json
{
  "data": {
    "ico": "51636549",
    "name": "Version Two s. r. o.",
    "legal_form": "Spoločnosť s ručením obmedzeným",
    "legal_form_code": "112",
    "address": {"street": "Hlavná 1", "city": "Bratislava", "postal_code": "81101", "country": "Slovensko"},
    "established_date": "2018-05-15",
    "terminated_date": null,
    "source_register": "Obchodný register",
    "status": "active",
    "orsr_id": "427482",
    "registration_date": "2018-05-15",
    "business_activities": "Počítačové služby...",
    "registered_capital": "5 000 EUR",
    "shareholders": [{"name": "John Doe", "address": "Bratislava", "share_amount": "5000 EUR", "share_percentage": "100%"}],
    "statutory_body": [{"name": "John Doe", "role": "konateľ", "address": "Bratislava"}]
  },
  "meta": {"rpo_updated_at": "2026-02-05T06:00:00Z", "orsr_synced_at": "2026-01-20T14:30:00Z", "source": "database"}
}
```

### Autocomplete Search

| Endpoint | Parameters | Description |
|---|---|---|
| `GET /search/autocomplete?q=` | `q` (Search query (min 2 characters)), `limit` (Max results (default: 10, max: 20)) | Search companies by name or IČO with autocomplete. Returns Select2-compatible format. |
| `GET /search/name?q=` | `q` (Company name (min 3 characters)), `exact` (Exact match (0 or 1, default: 0)), `limit` (Max results (default: 10, max: 100)), `offset` (Pagination offset (default: 0)) | Search companies by name with pagination. |
| `GET /search/ico?q=` | `q` (Partial IČO), `limit` (Max results (default: 10, max: 100)) | Search companies by partial IČO number. |
| `GET /search/advanced` | `name` (Company name), `city` (City name), `legal_form` (Legal form) | Multi-field search with various filters. |

```json
{
  "results": [
    {"id": "51636549", "text": "Version Two s. r. o.", "ico": "51636549", "city": "Bratislava"}
  ],
  "pagination": {"more": false}
}
```

### Batch (Requires Starter+ plan)

| Endpoint | Description |
|---|---|
| `POST /batch/ico` | Batch lookup of multiple companies by IČO. Requires Starter plan or higher. `{"icos": ["51636549", "..."]}` |
| `POST /batch/names` | Batch lookup of multiple companies by name. Requires Starter plan or higher. `{"names": ["Version Two s. r. o.", "..."]}` |
| `GET /batch/{batchId}/status` | Check the processing status of a batch operation. Returns current progress and state. |
| `GET /batch/{batchId}/results` | Retrieve the results of a completed batch operation. Results expire after a limited time. |

```json
{
  "data": {
    "51636549": {"found": true, "data": {"...": "..."}},
    "87654321": {"found": false, "data": null}
  },
  "meta": {"total": 2, "found": 1, "not_found": 1}
}
```

### Account

| Endpoint | Description |
|---|---|
| `GET /account/usage` | Get current period usage statistics. |
| `GET /account/quota` | Get remaining quota for current billing period. |
| `GET /account/history?days=` | Get usage history for the specified period. `days`: Number of days to retrieve (default: 30, max: 365) |
| `POST /account/thresholds` | Configure credit-usage alert thresholds (email when a chosen percentage of quota is reached). |

### Notifications

| Endpoint | Description |
|---|---|
| `GET /notifications` | List your active and historical notification subscriptions. |
| `POST /notifications` | Create or update a change subscription for a specific company IČO. You can configure a webhook URL, an email target, or both. |
| `DELETE /notifications/{ico}` | Delete a subscription for the specified company IČO. |
| `GET /notifications/{subscriptionId}/deliveries` | Retrieve recent deliveries for a specific subscription, including channel, status, and retry attempts. |
| `GET /notifications/history/{ico}` | Get detected company change history for the specified IČO. |
| `POST /notifications/test` | Validate a webhook or email destination without creating a persistent subscription. |

```json
{
  "ico": "51636549",
  "webhook_url": "https://example.com/firmapi/webhook",
  "notification_emails": ["ops@example.com", "alerts@example.com"],
  "events": ["updated"]
}
```

## Response Format & Data Model

Company endpoints return a JSON object with `data` and `meta` fields. The `data` object contains all available company information grouped into the following categories.

- **Basic Information** – Core company data from RPO: `ico`, `name`, `legal_form`, `legal_form_code`, `address` (street, city, postal_code, country), `status` (active, terminated, deleted), `established_date`, `terminated_date`, `source_register`.
- **Tax Information** – The `tax` section contains tax identifiers and VAT state: `dic`, `ic_dph`, `is_vat_payer`, `vies_valid`, `vies_verified_at`.
- **ORSR Details** – Detailed data from the commercial register (only for ORSR companies). The base response carries `orsr_id` and `registered_capital`; the `orsr` scope (or `all`) adds the registered people split by body: `statutory_body` (name, role, function, body_type, address, acting_method, appointed_at, effective_from, effective_to, current), `supervisory_board`, `shareholders` (equity holders only – name, address, share_amount, share_paid, share_currency, share_percentage, contribution_kind, deposit_lien, is_company, ico, stakeholder_type, effective_from, effective_to, current), `shares` (share issues), `procurators` (with acting_method), `liquidators`, `administrators`, `founders`, `branch_heads`, `legal_predecessors`, `other_stakeholders` and `business_activities`. Members of collective bodies carry a `function` (chairman / vice_chairman / member).
- **Contacts, accounts, and financials** – Optional enrichment sections based on plan: `bank_accounts` (IBAN, bank, published account), `financials` (year, revenue, profit, assets, employees_count).
- **Risk and compliance data** – Advanced enrichments may include `debtor_status`, `financial_statements` (latest, available_years), `insolvency`, `commercial_bulletin`, `public_contracts_summary`, and `procurement_summary`.
- **Meta Fields** – Response metadata: `rpo_updated_at`, `orsr_synced_at`, `enriched_at`, `source` (database, cache, or live), `cached`, `stale`, `retry_at`, and `stale_reason`.

## Notifications: webhook and email

FirmAPI supports change notifications through webhook URLs and email. For each IČO you can configure either delivery channel or both, and manage them via API or dashboard.

Configure notifications in the [dashboard](https://api.firmapi.sk/en/dashboard/api-keys) or via `POST /notifications`. Webhook subscriptions receive a unique HMAC secret for signature verification.

### Event Types

- `company.updated` – Fired when existing company data is modified (e.g. address change, new shareholders, status update)
- Delivery is sent to the configured `webhook_url`, the `notification_emails` array, or both.

### Payload Structure

Webhook payloads include the event type, timestamp, changed field with previous and new value, and a compact company snapshot. Email notifications carry the same event meaning, but are delivered as readable messages without HMAC headers.

```json
{
  "event": "company.updated",
  "timestamp": "2026-02-05T12:00:00Z",
  "data": {
    "ico": "51636549",
    "change_type": "updated",
    "field": "address",
    "old_value": "Hlavná 1, 81101 Bratislava",
    "new_value": "Mlynské nivy 5, 82109 Bratislava",
    "detected_at": "2026-02-05T06:15:00Z",
    "company": {"name": "Version Two s. r. o.", "legal_form": "Spoločnosť s ručením obmedzeným", "city": "Bratislava", "status": "active"},
    "tax_info": {"dic": "2120776680", "ic_dph": "SK2120776680", "is_vat_payer": true}
  }
}
```

### Headers

- `X-Webhook-Signature` – HMAC-SHA256 signature of the request body using your webhook secret
- `X-Webhook-Event` – Event type (e.g. `company.updated`)
- `X-Webhook-Delivery-Id` – Unique delivery identifier for idempotency

### Signature Verification

Every webhook request is signed using HMAC-SHA256. Verify the signature to ensure the request is authentic and has not been tampered with.

```php
$signature = $request->header('X-Webhook-Signature');
$payload = $request->getContent();
$expected = hash_hmac('sha256', $payload, $webhookSecret);

if (hash_equals($expected, $signature)) {
    // valid signature
}
```

### Retry Behavior

If a webhook endpoint returns a non-2xx status code, FirmAPI retries delivery with exponential backoff. Delivery history is available via `GET /notifications/{subscriptionId}/deliveries`. Email notifications are logged in the same delivery history with channel `email`.

## Rate Limits

Rate limits depend on your subscription tier. The following headers are included in all responses:

- `X-RateLimit-Limit-Minute` – Requests allowed per minute
- `X-RateLimit-Remaining-Minute` – Requests remaining this minute
- `X-RateLimit-Limit-Daily` – Requests allowed per day
- `X-RateLimit-Remaining-Daily` – Requests remaining today

## Errors

All errors return a JSON object with `error` and `message` fields.

| Status Code | Error | Description |
|---|---|---|
| 401 | `unauthorized` | Invalid or missing API key |
| 402 | `credits_exhausted` | Credit balance exhausted – add credits or enable overage billing |
| 403 | `forbidden` | Feature not available on your plan |
| 404 | `not_found` | Company not found |
| 422 | `validation_error` | Invalid request parameters |
| 429 | `rate_limit_exceeded` | Too many requests |
| 503 | `service_unavailable` | Service temporarily unavailable |

---

Interactive documentation with code examples: https://api.firmapi.sk/en/docs · Use cases: https://api.firmapi.sk/en/use-cases.md · Pricing: https://api.firmapi.sk/en/pricing.md · Data sources: https://api.firmapi.sk/en/what-data-we-provide.md
