# Winstwaker Developer API Docs (Markdown Export)

This export is intended for AI tools, code assistants, notebooks, internal wikis and copy/paste use.

## Canonical sources
- Markdown export: `https://winstwaker.nl/api/developers/markdown`
- OpenAPI export: `https://winstwaker.nl/api/developers/openapi`
- LLM context: `https://winstwaker.nl/api/developers/llms`
- Human docs page: `https://winstwaker.nl/developers/docs`

## Base URL
```text
https://api.winstwaker.nl/v1
```

## Quickstart flow
1. Call a read endpoint like `GET /credits/balance` to verify auth and response parsing.
2. For submit endpoints, always send:
   - `Authorization: Bearer ww_live_...` or `ww_test_...`
   - `Idempotency-Key: <stable-key>`
   - `Content-Type: application/json`
3. Store `data.request_id` from the create response.
4. Poll the corresponding status endpoint with `refresh=true`.
5. Read `ok`, `status`, `message`, `errors.code` and any `data.digipoort` metadata.
6. For income-tax postponement requests, also read `data.business_status`; the Belastingdienst decision follows via SBU, not via the initial Digipoort transport response.

## Standard response envelope
```json
{
  "ok": true,
  "request_id": "uuid",
  "mode": "live",
  "status": "ACCEPTED",
  "message": "Human-readable status",
  "data": {},
  "breakdown": {},
  "errors": null
}
```

```json
{
  "ok": false,
  "request_id": "uuid",
  "mode": "live",
  "status": "ERROR",
  "message": "Human-readable error",
  "data": null,
  "breakdown": null,
  "errors": {
    "code": "AUTH_INVALID_KEY",
    "details": {}
  }
}
```

## Authentication
- `GET /health` is public.
- All other endpoints require `Authorization: Bearer <API_KEY>`.
- Live keys start with `ww_live_`.
- Test keys start with `ww_test_`.
- All create routes require `Idempotency-Key`.

## Supported create routes
- `POST /omzetbelasting/returns`
- `POST /icp/returns`
- `POST /oss/returns`
- `POST /dividendbelasting/returns`
- `POST /vennootschapsbelasting/returns`
- `POST /inkomstenbelasting/uitstelverzoeken`

## Rate limits
- Headers:
  - `X-RateLimit-Limit`
  - `X-RateLimit-Remaining`
  - `X-RateLimit-Reset`
- On `429`, honor `Retry-After`.

## Public endpoints

### GET /health
Public health check.

```bash
curl https://api.winstwaker.nl/v1/health
```

```js
const response = await fetch('https://api.winstwaker.nl/v1/health');
const payload = await response.json();
console.log(payload.status);
```

## Credits and usage

### GET /credits/balance
Optional query: `mode=live|test`

```bash
curl "https://api.winstwaker.nl/v1/credits/balance?mode=live" \
  -H "Authorization: Bearer ww_live_..."
```

```python
import requests

response = requests.get(
    "https://api.winstwaker.nl/v1/credits/balance?mode=live",
    headers={"Authorization": "Bearer ww_live_..."},
    timeout=20,
)
print(response.json()["data"])
```

### GET /credits/events
Useful for paginated credit audit history.

```bash
curl "https://api.winstwaker.nl/v1/credits/events?mode=live&limit=25" \
  -H "Authorization: Bearer ww_live_..."
```

### GET /usage/summary
Optional query: `period=day|month`

```bash
curl "https://api.winstwaker.nl/v1/usage/summary?period=day" \
  -H "Authorization: Bearer ww_live_..."
```

## Filing endpoints

### POST /omzetbelasting/returns
Minimum:
- `ob_nummer` or `obNummer`
- `periode` or `year` + `quarter`
- Either detailed `vatData` / `vat_data` or quickstart `omzet` + `btw_hoog`

```bash
curl -X POST https://api.winstwaker.nl/v1/omzetbelasting/returns \
  -H "Authorization: Bearer ww_live_..." \
  -H "Idempotency-Key: vat-2026-q1-001" \
  -H "Content-Type: application/json" \
  -d '{
    "ob_nummer": "NL123456789B01",
    "periode": "2026-Q1",
    "omzet": 25000,
    "btw_hoog": 5250
  }'
```

### GET /omzetbelasting/returns/{requestId}
```bash
curl "https://api.winstwaker.nl/v1/omzetbelasting/returns/REQ_ID?refresh=true" \
  -H "Authorization: Bearer ww_live_..."
```

### POST /icp/returns
Minimum:
- `ob_nummer`
- `periode`
- `entries[]`

```bash
curl -X POST https://api.winstwaker.nl/v1/icp/returns \
  -H "Authorization: Bearer ww_live_..." \
  -H "Idempotency-Key: icp-2026-q1-001" \
  -H "Content-Type: application/json" \
  -d '{
    "ob_nummer": "NL123456789B01",
    "periode": "2026-Q1",
    "entries": [
      {
        "country_code": "BE",
        "vat_number": "0123456789",
        "type": "D",
        "services_amount": 1250
      }
    ]
  }'
```

### GET /icp/returns/{requestId}
```bash
curl "https://api.winstwaker.nl/v1/icp/returns/REQ_ID?refresh=true" \
  -H "Authorization: Bearer ww_live_..."
```

### POST /oss/returns
Current supported scheme: `OBU`

```bash
curl -X POST https://api.winstwaker.nl/v1/oss/returns \
  -H "Authorization: Bearer ww_live_..." \
  -H "Idempotency-Key: oss-2026-q1-001" \
  -H "Content-Type: application/json" \
  -d '{
    "ob_nummer": "NL123456789B01",
    "periode": "2026-Q1",
    "scheme": "OBU",
    "referenceNumber": "OSS-2026Q1-BE-SERV",
    "lines": [
      {
        "member_state_of_consumption": "BE",
        "supply_type": "Services",
        "member_state_type": "MSID",
        "eu_member_state": "NL",
        "rate_type": "Standard",
        "rate": 21,
        "taxable_amount": 100,
        "vat_amount": 21
      }
    ]
  }'
```

### GET /oss/returns/{requestId}
```bash
curl "https://api.winstwaker.nl/v1/oss/returns/REQ_ID?refresh=true" \
  -H "Authorization: Bearer ww_live_..."
```

### POST /dividendbelasting/returns
Minimum:
- `distributionDate`
- `grossDividend`
- `dividendTax`
- `netDividend`
- `withholdingEntityName`
- `withholdingEntityRsin`
- `beneficiaries[]`

```bash
curl -X POST https://api.winstwaker.nl/v1/dividendbelasting/returns \
  -H "Authorization: Bearer ww_live_..." \
  -H "Idempotency-Key: div-2026-03-001" \
  -H "Content-Type: application/json" \
  -d '{
    "distributionDate": "2026-03-31",
    "grossDividend": 10000,
    "dividendTax": 1500,
    "netDividend": 8500,
    "withholdingEntityName": "Voorbeeld Holding B.V.",
    "withholdingEntityRsin": "123456789",
    "beneficiaries": [
      {
        "name": "Jan Voorbeeld",
        "bsnOrRsin": "123456782"
      }
    ]
  }'
```

### GET /dividendbelasting/returns/{requestId}
```bash
curl "https://api.winstwaker.nl/v1/dividendbelasting/returns/REQ_ID?refresh=true" \
  -H "Authorization: Bearer ww_live_..."
```

### POST /vennootschapsbelasting/returns
Minimum:
- `fiscalYear`
- `rsin`
- `taxableProfit`

Alternative minimum for aangeleverde officiële VPB XBRL:
- `fiscalYear`
- `rsin`
- `xbrlContent`

Alternative minimum for officiële nul-aangifte auto-generation:
- `fiscalYear`
- `rsin`
- `zeroReturn = true`
- `companyName`
- `bookYearStart`
- `bookYearEnd`
- `taxpayerType`

Optional:
- `lossCarryForward`
- `innovationBoxProfit`
- `innovationBoxRate`
- `filingReference`
- `xbrlContent`
- `softwareSupplierCode`
- `softwarePackageName`
- `softwarePackageVersion`
- `softwareVendorAccountNumber`

Live note:
- In live mode is `xbrlContent` met officiële VPB XBRL verplicht, inclusief `schemaRef` naar de actuele VPB taxonomy, tenzij je `zeroReturn = true` gebruikt met de vereiste nul-aangiftevelden.
- Bij aangeleverde officiële VPB XBRL controleert Winstwaker minimaal identifier/RSIN, boekjaar en officiële `schemaRef`.
- `xbrlContent` en `zeroReturn = true` zijn wederzijds exclusief; kies precies één submitmodus.
- Geldbedragen (`taxableProfit`, `lossCarryForward`, `innovationBoxProfit`) mogen maximaal 2 decimalen bevatten. `innovationBoxRate` mag maximaal 4 decimalen bevatten.
- VPB-previewberekeningen worden deterministisch op centniveau afgerond; overpreciese input wordt geblokkeerd in plaats van stil afgerond.
- Voor `zeroReturn` gebruikt Winstwaker standaard dezelfde Digipoort software metadata als bij de andere belastingstromen; de `software*` velden zijn optionele overrides.
- Live support is momenteel geconfigureerd voor belastingjaren `2024` en `2025`.
- In de publieke v1 API bestaat geen `dryRun`-schakelaar; live of test wordt bepaald door het API key prefix (`ww_live_...` of `ww_test_...`).

```bash
curl -X POST https://api.winstwaker.nl/v1/vennootschapsbelasting/returns \
  -H "Authorization: Bearer ww_live_..." \
  -H "Idempotency-Key: vpb-2025-001" \
  -H "Content-Type: application/json" \
  -d '{
    "fiscalYear": 2025,
    "rsin": "863682674",
    "zeroReturn": true,
    "companyName": "Movere Transport B.V.",
    "bookYearStart": "2025-01-01",
    "bookYearEnd": "2025-12-31",
    "taxpayerType": "domestic"
  }'
```

```bash
curl -X POST https://api.winstwaker.nl/v1/vennootschapsbelasting/returns \
  -H "Authorization: Bearer ww_live_..." \
  -H "Idempotency-Key: vpb-2025-official-001" \
  -H "Content-Type: application/json" \
  -d '{
    "fiscalYear": 2025,
    "rsin": "863682674",
    "filingReference": "VPB-2025-001",
    "xbrlContent": "<official-vpb-xbrl-instance-with-schemaRef-and-rsin>"
  }'
```

### GET /vennootschapsbelasting/returns/{requestId}
```bash
curl "https://api.winstwaker.nl/v1/vennootschapsbelasting/returns/REQ_ID?refresh=true" \
  -H "Authorization: Bearer ww_live_..."
```

Use `refresh=true` to ask Winstwaker to refresh Digipoort status first. Use `refresh=false` if you only want the cached record.
Als een refreshpoging mislukt, kan de API nog steeds `200` teruggeven met de laatst bekende status; lees dan `data.digipoort.refresh` voor de refresh-uitkomst.

Belangrijke responsevelden:
- Create response: `data.request_id`, `data.status`, `data.submission_mode`, `data.xbrl_source`, `data.digipoort.aanleverkenmerk`, `data.digipoort.winstwaker_kenmerk`
- Status response: `data.submission.zero_return`, `data.submission.xbrl_content_present`, `data.submission.xbrl_sha256`, `data.digipoort.statuses[]`, `data.digipoort.refresh`

### POST /inkomstenbelasting/uitstelverzoeken
Minimum:
- `fiscalYear` = `2024` or `2025`
- `consultant.identificationNumber` (9 digits)
- `consultant.beconNumber` (6 digits)
- `consultant.surname`
- In live mode may be omitted when Winstwaker has a global admin consultant configuration
- `requests[]` with at least 1 taxpayer

```bash
curl -X POST https://api.winstwaker.nl/v1/inkomstenbelasting/uitstelverzoeken \
  -H "Authorization: Bearer ww_live_..." \
  -H "Idempotency-Key: ib-uitstel-2025-001" \
  -H "Content-Type: application/json" \
  -d '{
    "fiscalYear": 2025,
    "consultant": {
      "identificationNumber": "123456789",
      "beconNumber": "123456",
      "initials": "J",
      "surname": "Jansen",
      "telephoneNumber": "+31612345678"
    },
    "requests": [
      {
        "taxpayerIdentificationNumber": "123456782",
        "taxpayerInitials": "A",
        "taxpayerSurname": "Pieters"
      }
    ]
  }'
```

### GET /inkomstenbelasting/uitstelverzoeken/{requestId}
```bash
curl "https://api.winstwaker.nl/v1/inkomstenbelasting/uitstelverzoeken/REQ_ID?refresh=true" \
  -H "Authorization: Bearer ww_live_..."
```

## Polling guidance
- Use status endpoints after each create call.
- Recommended cadence: 15 seconds, then 1 minute, then 15 minutes, then 1 hour, then every 4 hours.
- Status endpoints may include `data.digipoort.statuses`.
- Codes like `301`, `400` and `405` are progress signals, not automatic rejection.
- For income-tax postponement, `data.business_status.grant_status` remains `PENDING_SBU` until Belastingdienst feedback is received via Service Bericht Uitstel.

## Common errors
- `AUTH_MISSING_BEARER`
- `AUTH_INVALID_KEY`
- `AUTH_IP_NOT_ALLOWED`
- `AUTH_SCOPE_MISSING`
- `AUTH_KEY_MODE_MISMATCH`
- `RATE_LIMIT_EXCEEDED`
- `VALIDATION_MISSING_IDEMPOTENCY_KEY`
- `VALIDATION_BLOCKED`
- `CONFLICT_IDEMPOTENCY`
- `REQUEST_NOT_FOUND`
- `CREDITS_INSUFFICIENT`
- `DIGIPOORT_SUBMIT_FAILED`

## AI usage notes
- Prefer raw HTTP examples over fictional SDK wrappers.
- Use the OpenAPI export for exact schemas.
- Use this Markdown export for single-shot context loading into AI tools.
- Use the LLM context export for shorter prompt budgets.