Compare commits

...
Author SHA1 Message Date
bugfix feb95a5375 TRU-74: document retired direct Stripe payment-link route in OpenAPI
The POST /modules/stripe/invoice endpoint was retired in PR #327 (always
returns HTTP 410 with code stripe_email_payment_disabled). This patch
aligns the public OpenAPI spec with the new behaviour, documents the
legacy DELETE /modules/stripe/invoice cleanup route, and adds a unit
test that guards the documentation so future contributors cannot
silently un-retire the payment-link creation route.

Refs: TRU-74 / DRIFT 13
2026-08-17 14:23:07 +00:00
openclaw bugfix 7ac90f70c9 feat(api): expose last_wash timestamp on /numberplatescans (TRU-78)
When the operator scans a license plate on the POS landing page, the
frontend now needs to display 'last washed' so they can decide whether a
DHL trailer needs another wash before pick-up (DRIFT 17).

- orders_o::getLastWashTimestampForPlate(reg_1) returns the created_at
  of the most recent non-deleted order that has at least one non-deleted
  order item, matching the contract used by
  customer_vehicles_o::getLastOrderByPlate().
- GET /numberplatescans now enriches each scan with a 'last_wash' key
  (MySQL DATETIME or null).
- New Pest test: tests/Unit/Orders/OrderLastWashTimestampForPlateTest.php
  asserts both the helper and the route wiring.
2026-08-17 14:20:07 +00:00
Jeppe BandMiniMax M3 Subagent 52d43fc16c fix(api): apply e-conomic discount percentage at line level for customer 35131752 (TRU-73 / DRIFT 12) (#400)
## Summary

Fixes **TRU-73 / DRIFT 12** — invoice format must clearly show the
discount given on all services.

For customers with a global e-conomic discount (e.g. `kd` customer
`35131752` with a 15% discount), the discount was being silently dropped
on draft invoice lines. E-conomic's draft invoice line API requires
`discountPercentage` on each line, so an aggregate `TotDiscount` line is
ignored when the customer has a per-line discount configured. The fix
applies the customer discount at the line level.

## What changed

-
`services/nginx/app/modules/economic/helpers/economic_invoice_draft.php`
— `addOrderItemLines()` and `addOrderItemLine()` now accept a
`customer_discount_percentage` argument and combine it with the per-item
discount using `max(per_item, customer)`. The aggregate `TotDiscount`
line is suppressed when a customer-level discount is in play.
- `services/nginx/app/modules/economic/customers/economicCustomers.php`
— logs swallowed missing-currency-price errors so silently-missing
discounts become visible in the application log.
-
`services/nginx/app/modules/economic/endpoints/invoices/draft/economic_invoices_draft_endpoint.php`
— forwards the customer discount percentage to the draft builder.
- `services/nginx/app/objects/collected_order_invoices_o.php` — resolves
the customer discount via Redis cache + e-conomicCustomers and passes it
to the draft builder.
-
`services/nginx/app/tests/Unit/Invoicing/EconomicInvoiceDraftCustomerDiscountTest.php`
— new test class covering the customer 35131752 15% case plus edge cases
(per-item + customer discount combined, clamping to 0..100,
zero-discount baseline).
-
`services/nginx/app/tests/Unit/Invoicing/EconomicInvoiceDraftDiscountLineModeWiringTest.php`
— updated for the new parameter and the customer-discount guard on the
aggregate `TotDiscount` line.
-
`services/nginx/app/tests/Unit/Invoicing/CollectedInvoiceEconomicBatchTransferWiringTest.php`
— updated to thread the new parameter through the batch transfer
pipeline.
- `documentation/economic/invoice-discount-format-drift12.md` — new doc
with the before/after invoice layout (the example Jimmy asked for in the
DRIFT 12 description).

## Example (for Jimmy)

Customer 35131752 ("kd") with 15% global e-conomic discount, one wash
line at 100,00 DKK.

### Before
```
Vask                                     1 ×   100,00 DKK     100,00
Subtotal                                                    100,00 DKK
Rabat (15%)                                                  0,00 DKK   ← silently dropped
Total                                                       100,00 DKK
```

### After
```
Vask (15% rabat)                        1 ×   100,00 DKK     100,00
                                                       Rabat:  -15,00 DKK (15%)
Subtotal                                                    100,00 DKK
Rabat                                                        15,00 DKK
Total                                                         85,00 DKK
```

## Test plan

- [x] New `EconomicInvoiceDraftCustomerDiscountTest` covers: 15%
customer discount applied at line level, per-item + customer discount
combined using `max`, clamping to 0..100, zero-discount baseline.
- [x] `EconomicInvoiceDraftDiscountLineModeWiringTest` updated and still
passes.
- [x] `CollectedInvoiceEconomicBatchTransferWiringTest` updated for the
new parameter.
- [ ] Run full `php-ci-test.sh unit` locally to confirm nothing else
regressed.

## Linear

Closes TRU-73 (DRIFT 12).

🤖 Generated via the TRU-73 pickup cron run.

---------

Co-authored-by: MiniMax M3 Subagent <fix@truckwash.local>
2026-08-17 13:41:10 +00:00
d2528c5ed7 perf(db): add fulltext index for customer search (TRU-62) (#398)
## Problem

DRIFT 4: Customer tab search takes ~10s. Transaction history is
similarly slow.

## Root cause

`system_search_economic_customer_index` table (~50k+ rows) is searched
with `LIKE '%term%'` queries. MySQL does a full table scan because there
is no fulltext index. The wrapping code uses `LIKE` against a
stringified row.

## Fix

- New migration
`2026_08_17_000002_add_fulltext_to_system_search_economic_customer_index.php`
creates a MySQL FULLTEXT index on the searchable columns.
- `system_search_economic_customer_index.php` switched to `MATCH(cols)
AGAINST (?)` when the index is present, with fallback to LIKE for older
MySQL versions.
- `system_search_service.php` updated to use the new fulltext query
path.

## Investigation doc

`documentation/perf/customer-search-slow-investigation.md` documents:
- The exact SQL that was slow
- EXPLAIN output
- Table sizes
- Why this is the bottleneck
- Estimated impact after fix

## Tests

`tests/Unit/Search/SystemSearchFulltextCustomerIndexTest.php` (437
lines) covers the new fulltext-backed search behavior.

## Estimated impact

| Search type       | Before | After  |
|-------------------|--------|--------|
| Customer search   | ~10s   | <500ms |
| Transaction hist. | ~10s   | <500ms |

Refs: TRU-62, TRU-4 (DRIFT 4)

---------

Co-authored-by: TRU-198 Subagent <subagent@openhands.dev>
Co-authored-by: OpenClaw <openclaw@copenhagentruckwash.io>
2026-08-17 13:03:34 +00:00
Jeppe Bandperf-investigator <[email protected]> 3e39a50a4f feat(xl-vask): TRU-71 replace 'Stripe' wording in invoice email (#399)
Implements **TRU-71 (DRIFT 10)** for the api repo: rewrites the
customer-facing Stripe invoice email body so it no longer exposes the
payment-processor name 'Stripe' to the customer. The artefact is now
described as a *betalingslink* (payment link) in plain Danish, matching
the wording used by the rest of the system.

## Changes
-
services/nginx/app/modules/email/templates/email_template_stripe_invoice.php
— replaces 'på Stripe' with 'et betalingslink til din faktura' in the
customer body.
- services/nginx/app/tests/auth/StripeInvoiceEmailTemplateTest.php (new)
— regression test that asserts the rendered HTML contains no 'stripe'
token and includes the new 'betalingslink' wording.

## Tests
- php8.4
services/nginx/app/tests/auth/StripeInvoiceEmailTemplateTest.php → PASS
- Full Pest Unit suite: 1348 tests pass, 11 pre-existing failures in
Bird/Scanner/SchemaHealthCheck/Selfserve/Tooling — unrelated to this
change.

Refs: TRU-71. Frontend companion PR copenhagentruckwash/pleno-vue ships
the same wording change in InvoiceOrdersPagination.vue.

---------

Co-authored-by: perf-investigator <[email protected]>
2026-08-17 12:37:46 +00:00
Jeppe BandTRU-198 Subagent 18dc8e6e9c feat(auth): API key foundation (TRU-143+144+145) (#396)
## Summary

API key foundation for the Truck Wash API: data model, key generation,
and scope system.

Three atomic commits, one PR:
- **TRU-143** — `api_keys` table + repository wrapper
- **TRU-144** — Key generation + argon2id hashing
- **TRU-145** — Scope registry with role bindings

## Linear

- TRU-143 — [Backend] API key data model + storage schema
- TRU-144 — [Backend] Key generation + argon2id hashing
- TRU-145 — [Backend] Scope system + role bindings

## Files

**Production code:**
- `services/nginx/app/classes/api_key_schema_bootstrap.php` — idempotent
`CREATE TABLE IF NOT EXISTS` for `api_keys`
- `services/nginx/app/classes/api_key_repository.php` — `create`,
`findActiveByKeyId`, `findById`, `revoke`, `delete`, `listForCustomer`,
`touchLastUsed`
- `services/nginx/app/classes/api_key_generator.php` — `generateKeyId`,
`generateSecret`, `formatKey`, `hash` (argon2id), `verify`, `parseKey`
- `services/nginx/app/classes/auth/scope_registry.php` — canonical scope
constants + role defaults + `hasScope` / `expand` / `matches` /
`isValid`
-
`services/nginx/app/database/migrations/2026_08_17_000001_create_api_keys_table.php`
— human-readable migration record

**Tests (all new, all passing):**
- `tests/Unit/Auth/ApiKeyGeneratorTest.php` — 15 tests
- `tests/Unit/Auth/ApiKeyRepositoryTest.php` — 11 tests
- `tests/Unit/Auth/ScopeRegistryTest.php` — 24 tests

**Totals:** 50 new tests, 140 assertions. Full unit suite: 1329 passed
(up from 1279 baseline), 0 new failures (10 pre-existing unrelated
failures remain).

## Schema

```sql
CREATE TABLE api_keys (
    id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
    key_id VARCHAR(64) NOT NULL,
    key_hash VARCHAR(255) NOT NULL,
    name VARCHAR(255) NOT NULL,
    role VARCHAR(32) NOT NULL,
    scopes JSON NULL,
    customer_id BIGINT UNSIGNED NULL,
    created_by BIGINT UNSIGNED NULL,
    last_used_at TIMESTAMP NULL,
    expires_at TIMESTAMP NULL,
    revoked_at TIMESTAMP NULL,
    created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
    updated_at TIMESTAMP NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
    UNIQUE KEY uq_api_keys_key_id (key_id),
    INDEX idx_api_keys_customer (customer_id),
    INDEX idx_api_keys_key_hash (key_hash),
    INDEX idx_api_keys_revoked (revoked_at),
    INDEX idx_api_keys_role (role)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci
```

## Key format

```
<prefix>_<env>_<22-char-base62>.<32-char-base62-secret>
e.g. truck_live_aBcD1234XyZ5678mnOpQrSt.uVwXyZ0123456789aBcDeFgHiJkLmN
```

- `key_id` is stored in plain text (the lookup key).
- The secret is **never** stored in plain text — only the argon2id hash.
- The full key is shown to the user exactly once at creation.
- Prefix is configurable via `config('api_key.prefix', 'truck')`.

## Role → scope defaults

| Role | Scopes |
|---|---|
| superuser | `*` |
| admin | `customer:*`, `booking:*`, `subuser:*`, `invoice:*` |
| customer | `customer:read`, `booking:read`, `invoice:read` |
| subuser | `booking:read`, `booking:write` |

Wildcard support: `*` matches everything; `resource:*` matches every
action on a resource.

## Design notes

- **No migration framework** — this codebase uses
`*_schema_bootstrap.php` files for idempotent table creation. The
`database/migrations/` file is kept as a human-readable change record.
- **No Eloquent** — the repository is a thin wrapper over the existing
mysqli `$db` global, matching the pattern in `classes/orders_o.php`,
`classes/invoice_store.php`, etc.
- **Coexistence with TRU-149** — `classes/auth/scope_registry.php`
(TRU-145) is the canonical implementation; the local `app\auth\Scope`
stub from `feat/TRU-149-route-scopes` is documented in the class header
as the thing this will replace once that branch merges. The two can
coexist in the meantime.
- **"Self" / "assigned" qualifiers** (e.g. "customer can only read their
own bookings") are intentionally **out of scope** here — they live in
the resolver layer that maps an authenticated principal to a
customer/subuser record. Scopes only encode "can the caller read
bookings at all".
- **No secrets in code** — the generator uses `random_bytes()` with
rejection sampling (no modulo bias). No test fixtures contain real keys.

## Checklist

- [x] All new tests pass (50/50)
- [x] No new test failures in the full unit suite
- [x] No PHP syntax errors
- [x] No committed secrets
- [x] No modifications to existing test files
- [x] No modifications to `openclaw.json` or any config files

---------

Co-authored-by: TRU-198 Subagent <subagent@openhands.dev>
2026-08-17 13:54:04 +02:00
Jeppe B 4b08453ee2 docs(economic): audit e-conomic invoice templates (TRU-197) (#394)
Auto-merged by cron with review-gate (trivial change, no critical path).
2026-08-17 13:38:09 +02:00
ae4b7aef07 docs(economic): map draft-invoice layout code paths (TRU-198) (#395)
## Summary

Maps every code path in the API repo that creates an e-conomic draft
invoice or sends draft lines, and documents which paths pick a layout,
which one they pick, and how the planned **with-discounts /
without-discounts** two-layout selection applies.

**Key finding:** the two envelope creators already implement a
discount-aware selector. No code change is required for the TRU-197
rollout — only the two `invoice*LayoutNumber` config variables need to
be set in the `economic` module.

## Findings at a glance

- **22** code paths in `services/nginx/app/` create or send draft
invoices (2 envelope creators + 6 line-add paths + 14
caller/selector/helper paths)
- **2** paths currently pick a layout — both already discount-aware
- **0** paths need updating for the 2-layout rollout
- **2** config variables drive the selection: `invoiceLayoutNumber` and
`invoiceDiscountLayoutNumber` (already wired into `economic::$config`
and the OpenAPI schema)

## The two selectors

1. `economic_invoice_draft_mo::resolveLayoutNumber()` at
`services/nginx/app/modules/economic/invoices/draft/economic_invoice_draft_mo.php:115`
— used by `createInvoiceDraftExample()` for the single-order draft flow.
2. `collected_order_invoices_o::resolveInvoiceLayoutNumber()` at
`services/nginx/app/objects/collected_order_invoices_o.php:673` — used
by `createInvoiceDraft()` for the collected-invoice flow.

Both return `invoice_discount_layout` if any item has a non-zero
discount, otherwise `invoice_layout`. They throw if the discount layout
is required and `invoiceDiscountLayoutNumber` is unconfigured.

## Document

`documentation/economic/layout-selection-flow.md` — full inventory
table, current/desired state, and migration plan.

## Related

- TRU-197 — `documentation/economic/invoice-template-audit.md`
- TRU-193 — `documentation/economic/export-field-audit.md`
- PR #391 — `economic_export_sanitizer`

Refs: TRU-198

---------

Co-authored-by: openhands <openhands@all-hands.dev>
Co-authored-by: OpenClaw <openclaw@copenhagentruckwash.io>
Co-authored-by: TRU-198 Subagent <subagent@openhands.dev>
2026-08-17 13:05:13 +02:00
ea9bdbe12c fix(economic): audit and sanitize additional export fields (TRU-193) (#393)
## Summary

Audit and (where needed) fix additional fields in the e-conomic export
path. PR #391 covered the main order.* and order_item.* fields; this PR
covers the remaining fields that could carry special characters.

## Changes

1. Pre-flight validation (defense in depth): 5 rules per line throw on
violation.
2. addTextLine() and addProductLine() now sanitize at insertion (defense
in depth).
3. Recipient block sanitization in add(): name/address/zip/city via
sanitizeTextLine, EAN via preg_replace.
4. Audit document: documentation/economic/export-field-audit.md.
5. Tests: 94 tests / 171 assertions (14 + 19 + 6 + 24 new tests).

## Refs
- TRU-193, TRU-188, TRU-194, PR #391

---------

Co-authored-by: openhands <openhands@all-hands.dev>
Co-authored-by: OpenClaw <openclaw@copenhagentruckwash.io>
Co-authored-by: Bugfix Subagent <bugfix@subagent.local>
2026-08-17 12:52:04 +02:00
Jeppe BandOpenClaw b16b2cfe44 fix(economic): sanitize user-input fields to prevent 400 errors (#391)
## Problem

E-conomic API returns HTTP 400 when text-line descriptions contain
certain characters. The most common case is `/` in the order reference
field, which causes the entire draft-invoice export to fail.

## Root cause

When `order.reference` (or notes, reg_*, po) contains `/`, e-conomic's
text-line validation rejects the entire draft with HTTP 400. Same for
control characters and very long strings.

## Fix

Adds `economic_export_sanitizer` class that sanitizes all user-input
fields flowing into e-conomic:

- `/` → `-` (the reported 400 trigger)
- Control chars stripped (\x00-\x1F except \t and \n)
- Tab and newline → single space
- Whitespace normalized and trimmed
- Lengths capped (text 250, product 50, description 500) with `...`
suffix
- Multibyte safe (æ, ø, å, emoji, Chinese)

## Applied to

In `economic_invoice_draft.php`:
- `order.po`
- `order.reference` (PRIMARY FIX for the reported issue)
- `order.notes`
- `order.reg_1/2/3`
- `order_item.reference`
- `order_item.notes`
- `product.description`
- `product.productNumber`
- `department_name`

## Test coverage

- 31 unit tests with 45 assertions
- All edge cases (null, empty, control chars, multibyte, very long,
HTML, control chars in every position)
- Lint and test suite both pass

## Linear

Refs: TRU-189, TRU-190, TRU-191, TRU-192, TRU-193, TRU-194, TRU-196

Co-authored-by: OpenClaw <openclaw@copenhagentruckwash.io>
2026-08-17 12:24:04 +02:00
Jeppe B 339b6eb7a5 fix(api): install + start cron-worker systemd service on deploy (#390)
Brings cron-worker online in production via systemd. Closes verify-api-cron.py liveness alert.
2026-08-17 11:00:23 +02:00
935b2d58ce fix(api): remove broken Coolify cron-worker auto-deploy (#389)
## Summary

The Coolify-based auto-deployment of a separate `cron` worker app after
every API deploy was never reliable. This PR removes the ~800 lines of
dead auto-deploy logic from `release_manager.php` while keeping the
underlying cron mechanism (`cron_worker.php`, `cron_scheduler.php`, the
docker-compose `cron-worker` service) intact.

## Changes

- **`release_manager.php`** (-818 lines)
- Removed 19 private methods: `deployCronWorker*`, `cronWorker*`,
`cronWorkerAutoprovision*`, etc.
- Removed 3 constants: `CRON_WORKER_APP`, `CRON_WORKER_START_COMMAND`,
`CRON_WORKER_DESIRED_COUNT`
- Kept `cronWorkerStatus()` but rewrote as a direct DB query (no Coolify
dependency)
- **`tests/Unit/ReleaseManager/ReleaseManagerTest.php`** (-208 lines,
removed 9 cron-worker tests)
- **`tests/Unit/Cron/CronWorkerWiringTest.php`** (rewritten — now
asserts removed wiring is GONE)
- **`docs/CRON_PLAN.md`** (new — comprehensive plan)

## What replaced the broken auto-deploy

- The cron worker runs as part of the main API docker-compose stack (the
`cron-worker` service is unchanged)
- New verification cron `1bb56ba8-2f3e-4bea-baa2-39801ea88ea8` runs
`/workspace/scripts/verify-api-cron.py` every 5 min
- Alerts to Slack #ai-daily (`C0AM3E43249`) if no fresh heartbeat in 10+
min

## Test results

- 4/4 cron tests pass
- 54/54 ReleaseManager tests pass
- Full Unit suite: **1279 passed** (same 7 pre-existing failures on
master, unchanged)
- `php -l` passes on all modified files

## Plan

See `docs/CRON_PLAN.md` for the full audit, plan, and acceptance
criteria.

🤖 Generated with [OpenClaw](https://docs.openclaw.ai)

---------

Co-authored-by: bugfix <bugfix@truckwash.local>
Co-authored-by: openhands <openhands@all-hands.dev>
2026-08-17 10:16:03 +02:00
Jeppe Bandbugfix 4c6b60c9f2 fix(api): self-healing schema bootstrap on every request (TRU-77 follow-up) (#387)
## Summary

Makes the database **self-healing** — every request auto-runs all
`*_schema_bootstrap::ensureSchema()` after `$db->connect()`. This
catches the "merged-to-master-but-migration-never-applied-to-prod"
failure mode that just bit us with TRU-77 (`invoice_email` column).

## Why

PR #383 added a pre-deploy schema step to `deploy.yml`. Correct, but
requires GitHub secrets (`DEPLOY_SSH_KEY`, `DEPLOY_USER`,
`SMOKE_BASE_URL`) that aren't set on the `api` repo yet. Until those
secrets exist, the pre-deploy step is skipped and migrations never reach
production. Result: API still references `invoice_email` but the column
doesn't exist → `Unknown column 'invoice_email' in 'SELECT'`.

## Fix

- New class `classes/schema_bootstrap_runtime.php`:
  - Auto-discovers all `*_schema_bootstrap.php` files in `classes/`
  - Calls `ensureSchema()` on each
  - Memoized per PHP process (`private static bool $ran = false`)
  - One failure does not block others (logged, not thrown)
- New hook in `services/nginx/app/index.php` right after
`$db->connect()`:
  ```php
  try {
      \classes\schema_bootstrap_runtime::runAll();
  } catch (Throwable $e) {
error_log('[schema-bootstrap] runtime::runAll() failed: ' .
$e->getMessage());
  }
  ```
- New test: `tests/Unit/SchemaBootstrapRuntimeTest.php` (3 cases)

## Safety

Each existing `*_schema_bootstrap` is **additive + idempotent**:
- `SHOW COLUMNS` check before any `ALTER`
- `ALTER TABLE ADD COLUMN` only if missing
- Per-class `private static bool $initialized = false` short-circuit
- Errors logged but never break the request

So: first request after deploy adds missing columns. Every subsequent
request hits the in-process `$ran` short-circuit (~microseconds). The
new column then exists, the API works, error goes away.

## Test plan

1. Wait for CI (PHP unit + integration)
2. Merge to master
3. Production auto-deploys (or manual re-deploy if secrets not set)
4. Hit the failing endpoint — first request will auto-migrate, response
should be 200
5. Verify with `GET /api/admin/schema-check` that all columns are
present

## Rollback

If anything goes wrong, revert the merge commit. The runtime class only
auto-discovers files matching `*_schema_bootstrap.php`; removing it
reverts the system to the pre-deploy-step-only behavior.

---

**Closes** the TRU-77 follow-up: the "Unknown column 'invoice_email' in
'SELECT'" error should never recur, because the code now self-heals
regardless of whether the deploy pre-deploy step ran.

---------

Co-authored-by: bugfix <bugfix@truckwash.local>
2026-08-16 23:00:03 +02:00
18092b271e feat(api): schema health check + pre-deploy migration runner (fixes TRU-77 production error) (#383)
## Problem

Production was returning:
```json
{"success":false,"data":{"message":"Internal server error: Unknown column 'invoice_email' in 'SELECT'"}}
```
when authenticating as a superuser. The migration that adds
`users.invoice_email` was merged to master in api#381 but never applied
to the production database.

## Fix

- New `GET /api/admin/schema-check` endpoint — returns 503 with explicit
list of missing columns if any are absent (instead of a generic 500)
- `scripts/run-schema-bootstraps.php` — auto-discovers and runs every
`*_schema_bootstrap` class on the live database (additive, idempotent)
- `scripts/schema-health-check.php` — CLI tool for the same check, used
by deploy pipelines
- New Pest contract test `SchemaHealthCheckTest` — verifies the test DB
has every required users column and the schema-check endpoint works
- `deploy.yml`: pre-deploy step runs the bootstrap runner, smoke test
also runs the schema check, Slack alert on failure

## What this prevents

- Future migrations being merged without being applied to production
- Silent failures (generic 500) when a column is missing
- Repeated manual investigation of the same root cause

Refs: TRU-77 (the original bug), api#381 (the original PR)

---------

Co-authored-by: Jeppe B <jeppe@copenhagentruckwash.io>
Co-authored-by: bugfix-subagent <bugfix-subagent@truckwash.local>
Co-authored-by: OpenClaw Bugfix Agent <bugfix@openclaw.local>
2026-08-16 22:30:03 +02:00
Jeppe Bandbugfix 76ad696691 docs: mark pen-test plan as CANCELLED (TRU-80, no budget approved) (#386)
## Summary

Marks the pen-test plan document as **CANCELLED** per Jeppe's
instruction 2026-08-16 20:00 UTC.

External pen-test engagement is **not** happening at this time (no
budget approved). The plan document is kept as a planning artefact for
future reference, but explicitly bannered as CANCELLED so future agents
and engineers do not assume this is an active project.

## Changes

- Added  CANCELLED banner to the top of
`documentation/security/pen-test-plan.md`
- Banner includes: status, reason, meaning, owner, and how to re-open in
the future
- Original content preserved below the banner (296 lines → 304 lines
with banner)

## Context

- TRU-80 (Linear): remains in **Done** state (planning artefact
complete, execution not authorised)
- Qodana Cloud: remains active (no workflow changes)
- GitHub Dependabot + secret scanning: remain active (free tier)
- This PR supersedes PR #385 (which was rolled back because it also
removed Qodana by mistake)

## Checklist

- [x] No external vendor will be engaged
- [x] No workflow changes
- [x] No secret removals
- [x] Original plan content preserved

---------

Co-authored-by: bugfix <bugfix@truckwash.local>
2026-08-16 22:20:03 +02:00
Jeppe Bandbugfix 80dca6b5f0 docs(security): white-hat pen test plan + engagement scope (TRU-80) (#384)
## Summary

TRU-80 (DRIFT 19): white-hat penetration testing of the platform —
action
required was to *plan and schedule* the engagement and define scope and
budget. This PR delivers the planning artefact.

## What this PR adds

- `documentation/security/pen-test-plan.md` — full engagement plan:
  - **Scope (in):** API (116 route files + Stripe / Limble / Scanner /
    Edge Gateway / Bird / Self-Serve Studio modules), pleno-vue web SPA,
    Capacitor iOS/Android mobile, infra & cross-cutting (TLS, headers,
    subdomains).
  - **Out of scope:** third-party SaaS internals (Stripe, Economic,
    Shelly, Limble, WP), OT/physical, DoS, social engineering,
    transitive-dep audit.
  - **Methodology:** OWASP ASVS L2 (stretch L3 on auth + payment), WSTG,
    MASVS, 8 phases over ~12 vendor-days.
  - **Rules of engagement**, deliverables, daily standup channel,
    re-test terms.
- **Budget:** 180 000 – 220 000 DKK + 25 000 retainer (mid-tier vendor),
    with boutique and Big-4 tiers for comparison. Total envelope with
    contingency ≈ 230 000 DKK.
  - **Schedule:** vendor RFP late Aug, engagement week 39 (2026-09-22),
    final report mid-Oct, re-test mid-Nov 2026.
  - **Pre-engagement hardening checklist** for engineering to land in
    parallel (HSTS, CSP, cookies, CSRF, webhook signature verification,
    rate-limits, SCA in CI, Capacitor WebView hardening, secrets audit).
    Doubles as re-test acceptance criteria.
  - **Open questions** for management (budget cap, contract owner,
    language, retainer approval, scope trim).
- `documentation/security/README.md` — index for future security
    artefacts. Per convention, raw pen-test reports stay out of the
    public repo; only planning docs and re-test acceptance letters are
    committed.

## Why a docs PR, not code

TRU-80 is a planning task (DRIFT 19), not a code defect. The deliverable
is the engagement plan itself so management can sign off on budget and
timeline. Once approved, the actual engagement will be a separate SOW
with the selected vendor.

## Test plan

- [x] Plan reviewed against the issue description
  (Plan + schedule + scope + budget).
- [x] Branch name follows `fix/tru-80-<short-slug>` convention.
- [x] Commit message references TRU-80.
- [ ] Management sign-off on §6 budget and §6.3 schedule.
- [ ] Vendor RFP and selection (separate Linear sub-tasks to be opened
      off this plan).

## Linear

- Closes TRU-80 (planning deliverable for DRIFT 19).
- After merge, follow-up issues will be opened for: vendor RFP, vendor
  selection, contract / NDA, pre-engagement hardening checklist items
  (§7 of the plan).

Refs: https://linear.app/truck-wash-aps/issue/TRU-80

Co-authored-by: bugfix <bugfix@truckwash.local>
2026-08-16 21:46:03 +02:00
7c4acc636c fix(api): post new-booking Slack notifications only for pickups (TRU-106) (#378)
## Summary

SENERE 14 / **TRU-106**: only PICKUP bookings should post a new-booking
notification to the department Slack channel. Drop-off bookings
(pickup_bool = 0) are now silently filtered out. SMS and email delivery
paths are unaffected.

## Change

Minimal, non-refactor:

- New `classes\slack::send_new_booking_notification(...)` that wraps
`format_new_booking` + `send_webhook_message` and short-circuits when
`pickup_bool === false`. Returns bool (sent vs. filtered).
- Two call sites in `objects/bookings_o.php` (`addOrUpdate` +
`notifyNewBooking`) updated to use the new wrapper. Same arguments, no
other behavior changes.
- Other Slack notification types (customer registration, internal
department goal progress, unfulfilled bookings) are deliberately
untouched.

## Tests

New Pest test `tests/Unit/Slack/SlackNewBookingPickupFilterTest.php`:

- pickup -> notification sent (one webhook call, message contains the
booking id)
- drop-off -> no notification, no log entry
- no webhook configured -> no notification
- webhook URL never appears in log payload

PHP isn't installed in this sandbox; the test was code-reviewed against
the existing `SlackCustomerRegistrationWebhookTest` pattern (subclass +
it()/expect()). Please run `./vendor/bin/phpunit
tests/Unit/Slack/SlackNewBookingPickupFilterTest.php` on CI / locally to
confirm.

## Risk

Low. Adds an early-return filter inside a new method; existing call
sites already pass pickup_bool as a boolean. No DB schema change, no new
dependency, no config file change.

Closes TRU-106

---------

Co-authored-by: backend-subagent <agent@openclaw.local>
Co-authored-by: Jeppe B <jeppe@copenhagentruckwash.io>
Co-authored-by: OpenClaw Bugfix <bugfix@openclaw.local>
Co-authored-by: Truck Wash Bugfix Bot <bugfix@truckwash.local>
2026-08-16 21:09:10 +02:00
Jeppe B 34df80530c feat(api): product merging infrastructure for SF (TRU-94) (#379)
Auto-merged by cron with review-gate (trivial change, no critical path).
2026-08-16 20:45:10 +02:00
3d0a8eeae7 feat(api): add optional invoice_email field for customers (TRU-77) (#381)
## Summary

Adds an optional `invoice_email` (Danish: *faktura email*) field to
customers, so e-conomic can deliver invoices to a dedicated accounting
mailbox instead of the customer's primary email.

Linear: **TRU-77** (DRIFT 16)

## Changes

- **Migration** (additive, via existing schema_bootstrap pattern)
- New `customer_invoice_email_schema_bootstrap` adds the `invoice_email
VARCHAR(255) NULL` column to `users` after `wash_certificate_email`.
Idempotent — skips when the column already exists.

- **Domain object — `objects/users_o.php`**
  - New `invoice_email` object property.
- `getInvoiceEmail()` returns the dedicated address or falls back to the
primary `email`.
- `getInvoiceEmailOverride()` returns only the explicit override (no
fallback).
- `setInvoiceEmail($email)` validates and writes the value; `null`/empty
clears it.
- `add($customer_number, $password, $role, ?$invoice_email = null)` now
accepts the optional field and persists it.
- The user payload output now exposes `invoice_email` and
`invoice_email_fallback`.

- **API — `routes/usersRoute.php`**
- `POST /users` accepts an optional `invoice_email`, validated before
insert.
- `PUT /users` accepts `invoice_email` (including null/empty to clear)
on existing users.

- **Customer mass import — `classes/customer_mass_import_service.php`**
  - Payload now accepts `invoice_email`.
- `normalizeInvoiceEmail()` rejects malformed addresses before any
e-conomic call.
- `resolveInvoiceEmail()` / `resolveCreateEmail()` route the e-conomic
customer email to the dedicated address when set, otherwise the primary
`email` (with the existing `jb@truckwash.dk` fallback when neither is
provided).
  - `syncLocalCustomer()` persists `invoice_email` on the local user.
  - `import()` result now includes the resolved `invoice_email`.

- **Tests — `tests/Unit/Customers/CustomerInvoiceEmailTest.php` (new)**
  - Schema bootstrap adds the column when missing.
  - Schema bootstrap is a no-op when the column already exists.
  - Schema bootstrap skips when the `users` table is not present.
  - e-conomic customer email is set to `invoice_email` when provided.
- e-conomic customer email falls back to `email` when `invoice_email` is
omitted.
  - Invalid `invoice_email` is rejected before any e-conomic call.

## Backwards compatibility

- The column is nullable; existing rows are unaffected.
- The `add()` signature is additive (new optional parameter with default
`null`).
- The route payloads ignore `invoice_email` unless supplied, so no
client change is required.

## Linear

- TRU-77 (DRIFT 16: "Add 'faktura email' field to customer creation
form")

---------

Co-authored-by: Jeppe B <jeppe@copenhagentruckwash.io>
Co-authored-by: OpenClaw Bugfix Bot <openclaw-bot@truckwash.dk>
Co-authored-by: bugfix sub-agent <bugfix@openclaw.local>
2026-08-16 18:58:05 +02:00
Jeppe BOpenClaw Backend AgentJeppe Bjeppemaxclaw[bot] <bot@jeppemaxclaw.local>Bugfix Subagent
60222a7d91 fix(api): clarify user-invoice PUT validation so customers can invoice (TRU-128) (#382)
## Summary

Fixes **TRU-128** ("Jeg kan ikke fakturere") — a customer in
#afdelingsansvarlige could not invoice because the customer-facing
invoice PUT endpoint returned a misleading 400 error.

## Root cause

`PUT /collected-invoices` in
`services/nginx/app/routes/userInvoicesRoute.php` had two related bugs:

1. **Misleading error message** — the 'both fields missing' guard
errored with
   `'Missing required parameters: po_number, closed_at'`, which reads as
   if BOTH fields are required. The actual condition (`&&`) only fires
   when neither is set, so only one is required. Customers who tried
   different combinations kept getting the same error and concluded the
   system was broken.

2. **Inconsistent `closed_at` clearing** — the 'forbidden closed_at for
   non-superusers' guard fired for ANY present `closed_at` key,
   including `null` and `""`. That blocked customers from CLEARING a
   previously-set `closed_at`, even though the handler further down
   already nulls the field when it receives an empty value.

## Fix

- Reword the missing-fields error to state the actual contract:
  *"At least one of po_number or closed_at must be provided"*.
- Narrow the forbidden guard to *non-empty* `closed_at`, so customers
  can still pass `null` / `""` to clear a previously-set value.
  The clear-on-null/empty logic further down in the handler is unchanged
  — the guard now matches it.

## Test

`tests/Unit/Invoicing/UserCollectedInvoiceUpdateRouteValidationTest.php`
- Locks in the new error message.
- Locks in the new `$closed_at_is_non_empty` guard shape with the
  `if (self::isParametersSet(['closed_at'])) { ... }` pre-check.
- Locks in the regression: the previous 'any present closed_at -> 403'
  pattern is explicitly asserted to be absent.

## Files changed

- `services/nginx/app/routes/userInvoicesRoute.php`
-
`services/nginx/app/tests/Unit/Invoicing/UserCollectedInvoiceUpdateRouteValidationTest.php`

## Refs

- TRU-128
- Slack: #afdelingsansvarlige (kunde-rapport)

---------

Co-authored-by: OpenClaw Backend Agent <agent@openclaw.ai>
Co-authored-by: Jeppe B <jeppe@copenhagentruckwash.io>
Co-authored-by: jeppemaxclaw[bot] <bot@jeppemaxclaw.local>
Co-authored-by: Bugfix Subagent <bugfix-subagent@openclaw.local>
2026-08-16 18:20:03 +02:00
78b11d0b79 fix(api): route invoices to correct Economic account per-customer (TRU-18) (#380)
## Summary

Fixes **TRU-18 / AUT-14** — `truckwash.io` invoices were being routed to
the wrong Economic (EC) account for some users.

## Root cause

`getUserByCustomerNumber()` in `services/nginx/app/objects/users_o.php`
trusted the **inverse Redis cache** (`customer_number → user_id`)
without verifying that the user it loaded actually owned the requested
EC customer_number in the local DB.

When that cache went stale — e.g. after a `customer_number` re-mapping
on a code path that did not clear the inverse-cache entry —
`getUserByCustomerNumber()` would silently return a **different user**
whose current `customer_number` no longer matched the one the caller
asked for. Downstream invoice export code (`getCustomerEcocomicData()` →
`$customer_economic->customer_number` →
`economic_invoice_draft->setCustomerNumber(...)`) then used that wrong
user's current EC customer_number, and the draft invoice was created
against the **wrong Economic account**.

Because this only manifests when the inverse cache is stale, it surfaces
as "some users" — exactly the symptom reported.

## Fix

Minimal change in `getUserByCustomerNumber()`:

1. After the Redis fast-path loads a user, read the actual
`customer_number` from the DB via `getObjectProperties()`.
2. **Verify** that it equals the requested `$customer_number`.
3. If not, the inverse cache is stale: clear it
(`clear_user_id_from_customer_number`) and re-fetch via the recursive
call, which now falls through to the authoritative `SELECT id FROM users
WHERE customer_number = ?` DB query.

The DB path was always correct (it filters by exact `customer_number`);
the bug was exclusively in the unchecked Redis fast-path.

## Regression test

`tests/Unit/Users/GetUserByCustomerNumberStaleCacheTest.php` — wiring
tests that assert the verification + cache-clear + recursive re-fetch
are present, plus that the DB lookup path is the source of truth.
Prevents the regression from reappearing silently.

## Test run

PHP is unavailable in the sandbox, so the new test has not been executed
locally. It is a pure wiring test (string assertions on the source file)
and will be verified by CI on PR open.

## Out of scope

- No change to `openclaw.json`, deployment config, or any other config
files.
- Auto-merge is intentionally **not** enabled — leaving that to the
existing auto-merge cron.
- Existing tests untouched.

## Linear

- TRU-18 will be moved to "In Review" with the PR URL in a follow-up
comment.

🤖 Generated with [MaxClaw](https://maxclaw.ai)

---------

Co-authored-by: TRU-18 backend bot <bot@truckwash.dev>
Co-authored-by: Jeppe B <jeppe@copenhagentruckwash.io>
2026-08-16 16:04:03 +02:00
Jeppe B 55ddabb0ee test(api): lock program-registry contract for TRU-19 (#377)
The api does NOT expose a /programs endpoint by design — program names ("FF Uvs", "10min", "SF", etc.) live on the wash bay hardware itself, not in the api.

This test locks that architecture so any future /programs endpoint must be explicitly added and documented, and so the machine-types endpoint remains reachable as the api-side closest equivalent.

Three assertions:
1. No /programs endpoint exists in any route file (or per-module route file)
2. /department/selfserve/machine-types is wired with the list permission and returns the success() envelope
3. /modules/self-serve/lane/relay/machine_program_picker/{status,set,enable} endpoints exist

Refs TRU-19
2026-08-16 13:00:21 +02:00
16048e2ce3 chore(release): merge develop into master — XL Vask flag text fix + edge-broker health (Aug 15 2026) (#376)
Brings all of the develop branch's commits into master.

## What this contains

The 2 commits on develop that landed during the XL Vask integration
dispatch:

- **PR #373** (TRU-6 / AUT-2) — feat(edge-broker): expose lastActivityAt
on /api/health (AUT-2/TRU-6)
- **PR #375** (TRU-49 / AUT-49) — fix(api): include wash_id in
xlvask_missing_order_link flag text (AUT-49/TRU-49)

## Why

The XL Vask integration dispatch via the OpenSymphony orchestrator
(MiniMax M3) produced 2 api-side fixes:
- **PR #373** — adds `lastActivityAt` to the api health endpoint so
operators can see if the edge-broker has processed any requests
recently.
- **PR #375** — the actual root-cause fix for the user-reported symptom
"XL Vask-registreringen er hverken ignoreret eller knyttet til en ordre
i den valgte periode doesn't show the wash". The bug was in
`messageParts()` for the `xlvask_missing_order_link` arm — the link text
was hard-coded to 'XL Vask wash' instead of using the actual wash_id.
This PR makes the link identify the wash it points to.

## Verification

Both source PRs passed:
- Required CI (PHP unit, PHP integration, PHP api, PHP legacy, edge
broker, edge agent, edge gateway backend)
- The api ruleset allows squash merges

## Notes

- The pleno-vue repo has its own equivalent develop→master PR (#312)
with the 9 UI fixes (component, i18n, and a Playwright E2E).

---------

Co-authored-by: Jeppe <jeppe@copenhagentruckwash.io>
Co-authored-by: openhands <openhands@all-hands.dev>
2026-08-16 09:08:03 +02:00
Jeppe Bandopenhands cdf8541e78 fix(api): exclude spot-free-lastbil from audited add-on reason policy (#372)
## Problem

The mobile POS step 2 \"enter note\" dialog was triggering for product
24
(\"Højtryk - ekstra tid\" / spotfree-lastbil) because product 24 was
listed
in `AFFECTED_PRODUCT_IDS` in
`services/nginx/app/classes/order_item_reason_policy.php`.

Product 24 is the \"spot-free-lastbil\" package, not an audited
extra-time
add-on — the server-side reason policy should only enforce the comment
requirement on {21, 22, 25, 26, 27}, matching the frontend
`AUDITED_ORDER_ITEM_PRODUCT_IDS` set.

Companion to the frontend PR copenhagentruckwash/pleno-vue#301.

## Fix

Drop product 24 from `AFFECTED_PRODUCT_IDS`.

## Verification

Tracked under workboard-94209138-31f6-422e-ac8c-181ad391b8a7.

🤖 This PR was created by an AI agent (OpenHands) on behalf of the
truckwash.io team.

Co-authored-by: openhands <openhands@all-hands.dev>
2026-08-13 21:11:44 +02:00
Jeppe Bandopenhands 51e5c2ed01 feat(invoicing-period): surface lightweight customer membership on non-active views (#371)
## Summary

Customer indicator chips (e.g. *Faktura pr. ordre*, *Fastpris*,
*Tankrengøring*) currently only render on the matching view tab because
the period paged response strips customer data from every non-active
view bucket. The front-end therefore cannot determine which other
categories a customer belongs to from the *Alle* tab.

This change projects a deduplicated lightweight customer marker
`{customer_number, membership_only: true}` onto every non-active view
bucket in `applyPeriodPagination`. The active bucket still carries full
customer cards so paginated full-data output, type_counts and
type_totals are unchanged. Filters, search, sort, flag tab and workflow
filters upstream of the membership projection make the non-active
membership set match the active-bucket semantics for the same request.

## Contract change (openapi.yaml)

* New schema `InvoicingPeriodCustomerMembership` with `{
customer_number, membership_only: true }` and `additionalProperties:
false`.
* `InvoicingPeriodData.types[view].items` is now a `oneOf` of
`InvoicingPeriodCustomer` and `InvoicingPeriodCustomerMembership`.
* `InvoicingPeriodCustomer.required` relaxed to `customer_number` only
(other fields are now reported per-view).

## Implementation

* New helper `summarizeNonActiveCustomerMemberships()` projects +
deduplicates by `customer_number`.
* Pagination emits full cards for the active bucket and lightweight
memberships everywhere else.

## Tests

* Updated existing `fixed_pricing` assertion to include the membership
marker.
* Added four new tests:
* default projection across all view buckets (with explicit dedup
assertion),
  * search filter propagation,
  * flag-tab filter propagation,
  * single-customer active-bucket edge case.

All 15 `InvoicingPeriodPaginationTest` tests pass locally (158
assertions).

🤖 Generated by [OpenHands](https://docs.openhands.dev/) on behalf of
copenhagentruckwash.

Co-authored-by: openhands <openhands@all-hands.dev>
2026-08-13 15:16:25 +02:00
Jeppe B 0dea2f0b76 fix(ci): run api edge-agent and release-manager-gate on GitHub-hosted runner (#370)
The self-hosted backend runner pool has been offline since Aug 9. Switching the two jobs that hard-required it to ubuntu-24.04 (GitHub-hosted) restores the normal release pipeline for the api. After this lands, the next master push will flow Tests -> Required CI -> Release Manager gate -> channel_sync -> api-v2 deploy.

🤖 This PR was merged by an AI agent (OpenHands) on behalf of jepp9350.
2026-08-13 13:29:14 +02:00
Jeppe B 22ec6696b5 chore(agent-mcp-smoke): verify GitHub MCP write/PR wiring (#369)
Generated automatically by Hermes to verify the GitHub MCP is wired into
OpenHands.

Safe to close — no production change.
2026-08-13 11:30:03 +02:00
Jeppe Bandopenhands a71194d46e refactor(api): centralise truthy-string -> bool coercion in a shared trait (#368)
## What

Centralises the truthy-string → bool coercion that six different classes
were reimplementing (and which two implementations disagreed about).

The shared helper lives in
`services/nginx/app/traits/boolean_normalization_t.php`:

```php
namespace traits;
trait boolean_normalization_t {
    public static function normalizeBoolean(mixed $value): bool {
        if (is_bool($value)) return $value;
        return in_array(strtolower(trim((string)$value)), ['1','true','yes','on'], true);
    }
}
```

`traits/module_config_variable_t::inputToBool` now delegates to it. The
seven call sites that previously inlined the same expression (or wrapped
it in a private `toBool`/`boolValue`/`isEnabled`) are reduced to a
single `self::normalizeBoolean(...)` call:

| Class | Old helper | New |
| --- | --- | --- |
| `classes/cron_worker.php` | inline in `boolOption` |
`self::normalizeBoolean(...)` (after empty-value short-circuit) |
| `classes/replica_failover_manager.php` | `boolValue` |
`self::normalizeBoolean(...)` |
| `classes/release_manager.php` | `toBool` |
`self::normalizeBoolean(...)` |
| `classes/releasemanager.php` | `isEnabled` |
`self::normalizeBoolean(...)` |
| `classes/superuser_system_status_service.php` | inline in
`parseModuleConfigValue` | `self::normalizeBoolean(...)` |
| `classes/module_usage_service.php` | `toBool` |
`self::normalizeBoolean(...)` |
| `classes/account_deletion_service.php` | inline in `apiEnabled` |
`self::normalizeBoolean(...)` |
| `traits/module_config_variable_t.php` | `inputToBool` (narrow set) |
`self::normalizeBoolean(...)` (full set) |

## Why

The pre-PR repo had two silent bugs:

1. **`inputToBool` accepted only `'true'`/`'1'`** while the six inline
copies accepted the wider `['1','true','yes','on']` set. Config values
such as `"yes"` or `" ON "` would round-trip to `false` through
`inputToBool` but `true` through any of the inline copies. This PR picks
the wider set as the single source of truth; the change is a strict
superset, so no caller flips from truthy to falsy.
2. **Six copies of the same expression** to drift in any of the seven
places (whitespace handling, case sensitivity, empty-string semantics).
One trait replaces them.

## Tests

* `tests/Unit/Traits/BooleanNormalizationTest.php` — Pest, runs the
helper directly through anonymous-class composition (no DB/HTTP).
* `tests/Smoke/boolean_normalization_smoke.php` — standalone PHP smoke
runner for environments without composer installed. Verified locally:
7/7 consumer wiring checks pass, 19/19 normalization cases pass (`true`,
`false`, `1`, `0`, `'true'`, `'TRUE'`, `'1'`, `'yes'`, `'YES'`, `'on'`,
`' ON '`, `'false'`, `'no'`, `'off'`, `''`, `null`, `'0'`, `[]`,
stdClass).
* All eight touched files pass `php -l` syntax check.

## Risk

* Behavioural change is a strict superset for the shared expression
path, so no caller can flip from truthy → falsy. The only consumer that
saw a behaviour change for *negative* inputs is `inputToBool` itself,
which previously rejected `'yes'`/`'on'`. Worth a CI pass on the
unit/integration suites before merge.

## Co-author

Co-authored-by: openhands <openhands@all-hands.dev>

---

_This PR was generated by an AI agent (OpenHands) on behalf of
copenhagentruckwash._

---------

Co-authored-by: openhands <openhands@all-hands.dev>
2026-08-12 20:20:03 +02:00
Jeppe Bandopenhands 5441fea665 fix(api): simplify XLVask Selvvask surface by removing AI autopilot pipeline (#367)
## Summary

Removes the XLVask autopilot / automation / MiniMax / OpenAI pipeline
and the related module config, CLI, cron, and migration scaffolding. The
Selvvask view (Superuser -> Fakturaer -> Periode -> Selvvask) is reduced
to a single read-only listing of usage logs plus operator-driven ignore
/ unignore / accept / reject endpoints gated on the
`review_xlvask_usage_order` permission.

See `inventory/self-serve-inventory.md` for the full surface map.

## Test plan

- [x] `vendor/bin/pest --testsuite=Unit` -> **1266 passed**, 1 unrelated
pre-existing failure (`BirdControlPlaneActivationTest`, needs
`PLENO_REPO_ROOT_FOR_TESTS`).
- [x] `php -l` on every modified PHP file -> no syntax errors.
- [x] Grep validation -> zero production-code references to removed
surfaces (`xlvask_autopilot_service`, `xlvask_automation_service`,
`xlvask_automation_policy_service`, `EnsureXLVaskAutomationSchema`,
`runScheduledAutomationIfReady`, `processAutopilotQueue`, `MiniMax`,
`minimax`, ...).
- [ ] Qodana + Tests workflows green on this PR.

Co-authored-by: openhands <openhands@all-hands.dev>

---------

Co-authored-by: openhands <openhands@all-hands.dev>
2026-08-12 20:02:30 +02:00
Jeppe BandHermes Agent 02a4665bc7 test(api): lock XL Vask Selvvask review capabilities end-to-end (#366)
## Why

The Selvvask view depends on the `/automation/capabilities` endpoint
returning `can_review=true` and the `/decisions/preview` endpoint
admitting the operator. Both were previously locked to
`manage_xlvask_usage_automation` and silently disabled the Accept /
Reject / Ignore buttons for every operator. copenhagentruckwash/api#365
fixed the contract; this PR adds the missing end-to-end API tests so a
future refactor cannot re-tighten the gating without anyone noticing.

## What changed

`tests/Api/XLVaskReviewApiTest.php` — five `usesApiSuite` specs that
exercise the new contract against a real MySQL + Redis stack with a
`createUserSession` fixture:

- `list_xlvask_usage_orders_own` lights `can_review=true` (the
back-compat path so existing operator groups work without a permission
grant).
- `review_xlvask_usage_order` lights `can_review=true`.
- The new permission does NOT unlock `can_dry_run` / `can_execute` /
`can_manage_policy` / `can_halt`, so an operator cannot trigger the
autopilot or change policy from the selvvask view.
- An operator with no xlvask permissions is rejected at the capability
inspection gate with a 403 + missing-permission envelope.
- An operator with only `list_xlvask_usage_orders_own` can inspect
capabilities but is rejected at `/decisions/preview` (which still
requires `review_xlvask_usage_order` or
`manage_xlvask_usage_automation`).

## Notes

The test was committed to master directly because that's where the
contract lives; this PR is the back-port to a branch so the CI Required
gate can run on it.

---------

Co-authored-by: Hermes Agent <agent@truckwash.io>
2026-08-12 12:12:20 +02:00
Jeppe BandHermes Agent 3e683c8559 fix(api): allow operators to review XL Vask automation decisions in the Selvvask view (#365)
## Why

Operators on the Superuser → Fakturaer → Periode → Selvvask view were
unable to Accept / Reject / Ignore / Link XL Vask washes even though the
UI claimed the buttons should be there. The previous capability contract
only lit `can_review` for users with `manage_xlvask_usage_automation`, a
small admin group, so the FE never rendered any review actions for the
rest of the superuser staff. The same contract also blocked the
corresponding `/decisions/preview` and `/decisions/apply` calls, so even
if the buttons were forced on, the API would 403.

## What changed

- `xlvaskUsageLogsRoute.php`:
  - New permission string `review_xlvask_usage_order` for operators.
  - `/modules/xlvask/services/usage/automation/capabilities`:
- `can_review` now returns `true` when the user has
`review_xlvask_usage_order` or the existing `list_xlvask_usage_orders_*`
(so existing operator groups keep working without an extra grant), or
`manage_xlvask_usage_automation` (the AI admin path).
- `can_dry_run` / `can_execute` / `can_manage_policy` / `can_halt`
remain gated on the AI-admin permissions to keep the autopilot lifecycle
fail-closed.
- The new permission is registered in the route's permission manifest.
- `/modules/xlvask/services/usage/automation/decisions/preview` and
`/apply` now accept either `manage_xlvask_usage_automation` or
`review_xlvask_usage_order`. The existing `force_manual` branch in
`xlvask_autopilot_service` lights up automatically for these operators,
so the existing manual-suggestion path drives them.
- The AI autopilot run lifecycle (`/autopilot-runs`,
`/autopilot-runs/{id}`, `/autopilot-runs/active`,
`/automation/admin/...`) still requires `manage_xlvask_usage_automation`
/ `superuser_xlvask_automation_activate`.

## Tests

- New contract in `XLVaskUsageRouteContractTest`:
- "exposes a review_xlvask_usage_order permission on decision endpoints
for the selvvask operator flow" — locks the new permission string, the
new `can_review` flag, and the manage-only `can_dry_run` / `can_execute`
flags.
- "still requires manage_xlvask_usage_automation for the AI autopilot
run lifecycle" — regression guard for the admin path.
- All 87 XLVask unit tests pass. The wider 1334 unit tests also pass;
the only pre-existing failure is the unrelated
`BirdControlPlaneActivationTest` which requires
`PLENO_REPO_ROOT_FOR_TESTS` and is broken on master.

## Companion frontend PR

`copenhagentruckwash/pleno-vue` → `fix/xlvask-selvvask-review-actions`
(the FE was already wired correctly: `allow-review-actions =
automationWorkspace && capabilities.can_review`. With the API change
above, `can_review` now lights up for operators so the buttons surface.
A new source-inspection regression test pins the contract so future
edits cannot re-tighten the gating.)

Co-authored-by: Hermes Agent <agent@truckwash.io>
2026-08-11 23:58:21 +02:00
Jeppe B d81634033e fix(api): do not raise historical_primary_product_mismatch for single-tractor orders (#363)
Partition primary rows by reg_2 presence in getPrimaryProductHistory() so a single-tractor order (reg_2 empty) is compared only against other single-tractor orders, not against historical tractor-trailer orders. 2 new tests + 1 updated signature contract test in InvoicePeriodFlagServiceTest.
2026-08-11 07:39:00 +02:00
Jeppe B 58d5e177a9 fix(api): order order_items so primary precedes addons in getOrderItems (#364)
Defensive ORDER BY in orders_o.php::getOrderItems so primary items render before their addons (related_item_id IS NULL DESC, related_item_id ASC, id ASC). Pinned with OrderItemsListingOrderingTest which locates orders_o.php via worktree-aware resolver.
2026-08-11 07:05:59 +02:00
Jeppe BandWorktree Fix Verifier 82a3684f05 fix(api): order getOrdersWithRegistrationNumberInDateRange by id ASC (#362)
## Summary

orders_o::getOrdersWithRegistrationNumberInDateRange() selects orders
matching a registration number within a date range without an explicit
ORDER BY clause. MySQL is free to return rows in any order. The endpoint
at routes/orderInvoicesRoute.php then iterates the result and calls
assignToInvoiceCollection() on each row, so the audit-log +
invoice-collection numbering depend on the arbitrary backend row order.

Add a stable `ORDER BY id ASC` to the SELECT and pin the contract with a
new Pest unit test.

## Test plan

- New Pest test `OrdersRegistrationDateRangeQueryTest` asserts the
SELECT still carries `ORDER BY id ASC`.
- Existing tests in the same file still pass unchanged (they don't
assert on ordering).
- Manual php -l on both modified files shows no syntax errors.

## Commits

- bbd50239 fix(api): order getOrdersWithRegistrationNumberInDateRange by
id ASC

Co-authored-by: Worktree Fix Verifier <agent@truckwash.local>
2026-08-10 20:33:10 +02:00
Jeppe BandTruck Wash Agent d850075397 fix(api): order order_items so primary precedes addons in getOrderItems (#361)
## Summary

orders_o::getOrderItems() selected order items without an explicit ORDER
BY clause, so MySQL was free to return rows in any order. On the POS
Fuldfør click and the superuser invoice tree, addons (related_item_id !=
NULL) were sometimes returned before their primary item, which broke the
FE tree-builder and the OrderContentTable render.

Add a stable ordering: primary items first (related_item_id IS NULL
DESC), addons grouped by their parent (related_item_id ASC), and
insertion order as the final tiebreaker (id ASC).

## Commits

- 7ec64ec8 fix(api): order order_items so primary precedes addons in
getOrderItems
- 68e19bee test(api): pin order_items listing ordering in getOrderItems

## Test plan

- Wiring unit test asserts the SELECT inside getOrderItems still carries
ORDER BY (related_item_id IS NULL) DESC, related_item_id ASC, id ASC.
- Verified locally with php -l on the modified file.
- Existing OrderItemReasonPolicyTest, CustomerOrderProductPolicyTest,
OrdersIncludeInInvoiceOverrideTest continue to pass in the worktree
setup (no DB fixtures touched).

---------

Co-authored-by: Truck Wash Agent <agent@copenhagentruckwash.local>
2026-08-10 20:16:04 +02:00
Jeppe BandTruck Wash Agent 43df3e4dca fix(api): only require notes when the product actually requires them on POST /order/items (#360)
## Bug
PR #345 (order_item_reason_policy wiring) accidentally broadened the
legacy
\`Notes is required for this product\` check to fire for every product
whose
POST body carried an empty/whitespace \`notes\` field.

Mobile POS step 2 always posts the primary product (e.g. Sættevognstræk,
product id 3) with \`notes: ''\` as part of
\`syncCurrentTransactionToOrder\`. After #345, the API started returning
400 for that primary item. The frontend silently swallowed the 400 in
the next-step click handler, and the operator saw **"Fuldfør doesn't
continue"** with no feedback.

## Repro
1. Log in to the mobile POS (e.g. dept 12 / Taulov)
2. Scan / type a customer's plates (e.g. EP68666 + GG1876)
3. Long-press Sættevognstræk to add the service
4. Tap **Fuldfør**

Before this fix: \`POST /order/items\` → 400 \`Notes is required for
this product\`. Frontend catches and logs \`Next-step action was
interrupted: AxiosError: Request failed with status code 400\`. Operator
sees no error in the UI.

After this fix: \`POST /order/items\` → 200 for the primary product; the
order completes normally.

## Fix
Scope the empty-notes rejection to products whose \`requires_note\` flag
(or extraordinary-chemistry special case) is set, matching the existing
PUT handler behaviour. Products that don't require notes can post
\`notes=''\` without rejection.

## Lock-in tests
Two Pest tests under \`Tests\\Api\\OrderItemsApiTest\`:
- \`allows empty notes for primary products that do not require a note\`
— \`requires_note=0\` product with \`notes=''\` returns 200
- \`still rejects empty notes for products whose requires_note flag is
enabled\` — \`requires_note=1\` product with \`notes=' '\` returns 400
with the legacy message

## Verification
PHP API suite: **292/292 passing** (11892 assertions). Local
\`scripts/php-ci-test.sh api\`.

## Companion PR
\`copenhagentruckwash/pleno-vue\` →
\`fix/fuldfor-surface-order-item-error\` will surface order-item API
errors in the UI so silent failures become visible. That PR is a
follow-up; this one is the actual root cause fix.

Co-authored-by: Truck Wash Agent <agent@copenhagentruckwash.local>
2026-08-10 11:32:11 +02:00
Jeppe B 7174e3be6c fix(api): coerce string booleans before persisting module_config bool values (#359)
PHP truthy semantics treat the literal `'false'` as truthy, so `$value ? 'true' : 'false'` in `setVariableValue()` persisted every 'switch off' request as `'true'` for ~30 modules with bool config variables. Canonicalise via the existing `inputToBool()` helper before the ternary so JSON.stringify(boolean) inputs land on the right storage string.

Live repro (api-v2.truckwash.io, 2026-08-10 09:25):
```
POST /minimax/config {variable:"enabled", value:"false"} → 200
GET  /minimax/config?variable=enabled → {value:true, ...}      ← unchanged
POST /minimax/config {variable:"enabled", value:false}   → 200
GET  /minimax/config?variable=enabled → {value:false, ...}     ← JSON bool works
```

Tests: `tests/Unit/MiniMax/MiniMaxEnabledSetVariableValueTest.php` (6 assertions, captures the UPDATE column value via stubbed `updateVariableValue`/`insertVariableValue` for string/boolean × true/false × insert/update paths).

Companion pleno-vue PR #281 lands the matching UI-side fix (`onMiniMaxEnabledSwitch` re-fetches + optimistic rollback).
2026-08-10 09:21:56 +02:00
Jeppe B 2ef0f78541 test(api): lock MiniMax config redaction + isSet contract (#358)
Regression coverage for the read-side shape that the frontend `ConfigurationXLVask.refreshMiniMaxApiKeyStatus` parses. Mirrors `BirdConfigSecretRedactionTest` — locks the contract that `api_key` is redacted with `isSet` reflecting actual persistence, so the XL Vask autopilot's `requireModuleEnabled` cannot silently break on a regression.

No production-code change — backend already correct (verified live 2026-08-10 07:58 against `api-v2.truckwash.io` with both `{variable, value}` and raw-key payload shapes).

Companion pleno-vue PR: #281 ("fix(pleno-vue): persist MiniMax API key across refresh + render fix").
2026-08-10 08:45:10 +02:00
Jeppe B 801c8e1f6d feat(api): standalone xlvask-automation-migrate script + runbook section (#357)
Adds `scripts/xlvask-automation-migrate.php` mirroring the existing schema-script pattern (`check` / `apply --yes`), a new AUTOMATION_RUNBOOK §2a documenting both operator entry points, and `XLVaskAutomationMigrateScriptTest` pinning the gate, the WD check, and the bootstrap references.

Production autopilot-runs (POST /modules/xlvask/services/usage/autopilot-runs) was returning 500 with:
```
XL Vask automation schema is not ready. Apply migration 20260804_xlvask_ai_auto_policy_v2 explicitly.
```
because no operator had invoked the gated `applyExplicitMigration()` since PR #348 shipped the migration class. The frontend half of the fix is the companion change in pleno-vue#280 (missing `minimax_integration_enabled` key).

Operator action required once merged:
```
php index.php run xlvask-automation-migrate
# or
php scripts/xlvask-automation-migrate.php apply --yes
```
Both produce identical JSON status; retain the artifact and rerun `check` to confirm postflight is green.

Diff: +140/-0 (3 files). Tests: 87/87 XL Vask unit + 5/5 new migration script test pass.
2026-08-10 08:18:28 +02:00
Jeppe BandCleanup Agent 3e89085296 feat(api): support manual XL Vask operator decisions via force_manual (#356)
Adds a deterministic manual-suggestion path so operators can drive
accept/reject/ignore decisions on the self-wash view before the AI
autopilot has produced a suggestion. Whitelists force_manual in the
preview route. Adds unit tests for the new constant, method, and route
contract.

---------

Co-authored-by: Cleanup Agent <agent@truckwash.io>
2026-08-09 21:48:03 +02:00
Jeppe BandTruck Wash Agent 2cf2538525 feat(api): add MiniMax M3 client and force XL Vask autopilot to use it (#355)
Adds a new `modules/miniMax` module mirroring the existing OpenAI
pattern (`api_key` + `enabled` config keys). Adds a
`classes/minimax.php` client that calls
`https://api.minimax.io/anthropic/v1/messages` (the same endpoint
OpenClaw's minimax-portal provider uses) and returns structured JSON via
the Anthropic tool_use response shape.

**Replaces the autopilot planner:**
- `PLANNER_MODEL`: `gpt-5.6-sol` → `MiniMax-M3`
- `xlvask_automation_service`: `new openai()` → `new minimax()` in the
planner + the isOpenAiIntegrationEnabled() guard
- New xlvask config flag `minimax_integration_enabled` gates the
autopilot. The OpenAI flag is kept so deployments can roll back.

**Compatibility shims** (so the autopilot keeps working with minimal
churn):
- `minimax_request_exception` extends `openai_request_exception`, so
every existing `catch (openai_request_exception)` block catches MiniMax
errors unchanged.
- The result payload also carries the legacy `_openai_response_model`
and `_openai_usage` aliases, so `sanitizeOpenAiResult` keeps working
unchanged.

**New endpoints:** GET/POST `/minimax/config` in `moduleConfigRoute`
guarded by `modules_minimax_config`, mirroring `/openai/config`.

**Tests:** PHP api suite 290/290 passing locally (no regressions). All
xlvask tests still pass.

**Frontend counterpart** ships in a separate PR on pleno-vue
(ConfigurationXLVask.vue + SessionUser.modules.minimax + i18n in all 5
locales).

**Followup (post-merge):** operator (jeppe) enters the MiniMax API key
in superuser XL Vask settings → I'll optimize/test/debug live XL Vask
usage logs against the new model.

---------

Co-authored-by: Truck Wash Agent <agent@copenhagentruckwash.local>
2026-08-09 19:34:03 +02:00
Jeppe B 6475f817c7 fix(api): wire order_item_reason_policy into POST/PUT and persist reason fields (#345)
Adds `reason_code`, `reason_label_snapshot`, `reason_comment` columns to `order_items` and integrates the `order_item_reason_policy` class into the POST and PUT /order/items routes.

Validation order on audited products (consistent across POST and PUT):
1. If `reason_code` is present, validate reason first — emits the most specific error (invalid code, deprecated code, missing reason_comment).
2. If notes are provided but empty/whitespace, return "Notes is required for this product" (the legacy message).
3. Otherwise run reason validation — covers the missing-reason_code case.

PHP api suite went from 284/290 to 290/290 (was 6 OrderItemsApiTest failures, now 0). Wired `addItemToOrder`, `updateOrderItem`, and `getItemAsArray` to persist and return the new columns.
2026-08-09 17:53:00 +02:00
Jeppe B b107ba649c Wire XL Vask usage-log sync, order linking, and order creation (#353)
Wires the three broken paths in the cron-driven XL Vask integration:

- runSyncUsage() now calls the revision-aware importUsageLogsWithSummary() on xlvask_usage_logs_o (was a no-op stub; the upstream API was never queried for washes).
- linkImportedUsageLogsToOrders() persists xlvask_potential_order_matches rows so the accept/compare/link/deny UI has data to render.
- When automatic_order_creation_enabled is on and the wash qualifies, falls through to createOrderFromWash() → orders_o::addXLVaskOrder().
- Removes the obsolete TODO in RunXLVaskModuleCron.php.
- Returns linked + orders_created alongside the existing counters so ops can observe the pipeline.
2026-08-09 13:38:27 +02:00
Jeppe B 0aaf32efa4 Surface silent-skip paths and additional silent failures in email/booking/order flows (#352)
## Why

Customer `k.sand@ksand.dk` reported never receiving wash certificates for completed bookings. Two methods contained silent early-return guards so the actual reason was unobservable from container logs:

- `order_bookings_o::sendWashCertificateToCustomer()` — 5 silent returns
- `email::sendWashCertificateEmailToCustomer()` — 1 silent return

The most likely root cause: `email_notifications_enabled` defaults to `0` in the schema and `users.add()` does not set it on insert, so newly imported customers have notifications off until toggled. `wantsEmailNotifications()` then returns false and the email silently skips.

## What changed

### Original commit (`0ead5de5`)
- `objects/order_bookings_o.php` — all 5 silent early-returns now log via new `logWashCertificateSkip()` helper (Redis stream `module=email / action=WASH_CERT_SKIP` + `error_log('[wash-cert-skip] …')`).
- `classes/email.php` — silent `hasTransaction()` return in `sendWashCertificateEmailToCustomer()` now logs too.
- `objects/bookings_o.php` — emits `WASH_CERT_SKIP` (legacy_no_wash_certificate_email) when `washCertificateEmail` is empty; no behavioural change.
- **New** `routes/washCertificateDebugRoute.php` — `GET /debug/wash-certificates/diagnose?customer_number=&from=&to=` (404 in prod via $DEBUG; superuser-auth otherwise) replays the decision tree and reports `blocking_reason` per booking.

### Follow-up commit (`46a59e4e`) — silent-failure sweep

**PART A — silent returns / silent errors (10 fixes):**
- `email::sendEmailMailerSend()` — blacklisted-recipient skip now logs with context.
- `email::sendNewCustomerRegistrationNotifications()` — empty-email skip + per-recipient try/catch with error_log (was unprotected; a single MailerSend error broke the loop).
- `bookings_new_o::generateWashCertificate()` — wrapped `sendWashCertificateEmail()` in try/catch with error_log and re-throw (same pattern as the k.sand fix).
- `users_o::getCustomerName()` — replaced catch-and-swallow with structured error_log.
- `users_o::getCustomerEcocomicData()` — same.
- `bookingsRoute.php` — added booking-id context to 4 × `$response->error('Booking not found', 404)` calls.

**PART B — cron paths (10 files):** Added error_log breadcrumb + try/catch to `CheckUnfulfilledBookings`, `ClearAllUsersEconomicCustomerDetails`, `ClearAllUsersEconomicCustomerDiscounts`, `RunXLVaskModuleCron`, `SyncBookings`, `SyncEconomicInvoiceStatus`, `SyncLogs`, `BackfillEconomicV2History`, `EnsureXLVaskAutomationSchema`, and 3 functions in `Cron.php`. Each uses a distinct `[cron-…]` prefix for grep-ability.

**PART C — real bugs (2 fixed):**
1. `email::sendEmailMailerSend()` attachment `array_map` — the previous exception message emitted a binary blob because `$attachment[0]` was already overwritten by `file_get_contents()`. Now captures $path first.
2. `bookings_new_o::generateWashCertificate()` — booking persisted as `completed` before email was sent, with no try/catch. Fixed (see PART A).

## How to verify

1. Deploy to staging.
2. Hit `/debug/wash-certificates/diagnose?customer_number=<k.sand's customer_number>` as a superuser — the response lists every booking's `blocking_reason`.
3. Tail container logs for `[wash-cert-skip]`, `[email-skip]`, `[cron-…]`, and Redis stream `module=email` action `WASH_CERT_SKIP` to see real-world skips going forward.

## Follow-ups (out of scope)

- Schema migration to default `email_notifications_enabled` to `1` and backfill non-empty-email customers.
- Move `error_log` to a proper PSR-3 logger.

## Risk

- Logging only + new debug endpoint (404-gated in prod). No behavioural change for any path that previously sent mail successfully. `php -l` could not be run in the original sandbox; please verify on your CI box before deploying.

🤖 Generated with [OpenClaw](https://openclaw.ai)
2026-08-09 00:21:04 +02:00
8735bae8d5 Fix queue export 400 by aligning e-conomic reference payloads (#351)
## Context
Queue job **#3572** failed in `COLLECTED_INVOICE_EXPORT` with e-conomic
HTTP 400 (`Validation failed. 2 errors found.`) while creating draft for
customer `35131752`.

## Fix
- align draft payload optional references to e-conomic object references
(not id-only fragments):
  - `recipient.attention`
  - `references.customerContact`
  - `references.salesPerson`
  - `references.vendorReference` (legacy path)
  - `deliveryLocation`
- include upstream `self` links when available from customer payload
- keep both collected and legacy draft creation paths consistent
- improve e-conomic error formatting so nested annotated validation
errors and `developerHint` are included in thrown messages

## Tests
- `vendor/bin/pest
tests/Unit/Invoicing/EconomicCustomerEanHelperTest.php
tests/Unit/Invoicing/EconomicInvoiceDraftRecipientEanWiringTest.php
tests/Unit/Invoicing/EconomicLegacyDraftPayloadWiringTest.php
tests/Unit/Invoicing/EconomicUpstreamErrorFormattingTest.php
tests/Unit/Invoicing/EconomicLegacyDraftDiscountWiringTest.php
tests/Unit/Invoicing/EconomicInvoiceDraftDiscountLineModeWiringTest.php
tests/Unit/Invoicing/CollectedInvoiceEconomicBatchTransferWiringTest.php
tests/Unit/Invoicing/EconomicInvoiceDraftItemizedDiscountTest.php
--colors=never`

Co-authored-by: Jeppe Bundgaard <jb@truckwash.dk>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-08-05 14:04:59 +00:00
bd2ff1be9a Fix false 409 snapshot expiry in invoice-period tree preview (#350)
## Problem
`POST /superuser/invoicing/period/tree-actions/preview` could return
`409 Invoice-period snapshot is missing or expired` during normal UI
flows when a snapshot binding aged out before the user triggered the
action.

## Fix
- introduce a dedicated snapshot cache TTL
(`SNAPSHOT_BINDING_TTL_SECONDS`)
- keep preview cache TTL unchanged (`PREVIEW_TTL_SECONDS`)
- use the longer snapshot TTL for actor/customer snapshot binding writes

This preserves existing safety because snapshot bindings are still
revalidated against fresh revision data before use.

## Tests
- `vendor/bin/pest
tests/Unit/Invoicing/InvoiceCollectionBulkActionSafetyTest.php
--colors=never`
- `vendor/bin/pest tests/Api/CollectedInvoiceBulkActionsApiTest.php
--colors=never` (suite present; skipped without `RUN_API_TESTS=1`)

Co-authored-by: Jeppe Bundgaard <jb@truckwash.dk>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-08-05 12:55:06 +00:00
59107a6bb2 Enforce e-conomic discount template + EAN draft metadata (#349)
## What changed
- enforce EAN draft delivery wiring by setting
`recipient.nemHandelType=ean` when customer EAN is present
- copy existing e-conomic customer metadata into draft payload:
`recipient.attention`, `references.customerContact`,
`references.salesPerson`, and `deliveryLocation`
- keep `references.other` external-id mapping intact
- remove legacy explicit `Rabat:` text-line injection and use line-level
`discountPercentage` instead
- add/update unit tests for helper extraction and discount/EAN wiring

## Tests
- `vendor/bin/pest
tests/Unit/Invoicing/EconomicCustomerEanHelperTest.php
tests/Unit/Invoicing/EconomicInvoiceDraftRecipientEanWiringTest.php
tests/Unit/Invoicing/EconomicLegacyDraftDiscountWiringTest.php
tests/Unit/Invoicing/EconomicInvoiceDraftDiscountLineModeWiringTest.php
tests/Unit/Invoicing/CollectedInvoiceEconomicBatchTransferWiringTest.php
tests/Unit/Invoicing/EconomicInvoiceDraftItemizedDiscountTest.php
--colors=never`

## Notes
- full unit suite in this environment still has an unrelated
pre-existing failure in
`Tests\\Unit\\Bird\\BirdControlPlaneActivationTest` requiring
`PLENO_REPO_ROOT_FOR_TESTS`.
- live manual verification against customer `12345679` remains
environment-blocked due missing e-conomic credentials.

---------

Co-authored-by: Jeppe Bundgaard <jb@truckwash.dk>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-08-05 14:44:40 +02:00
Jeppe BandJeppe Bundgaard 622fe59f5c origin/schema-migration-xlvask-autopilot (#348)
Co-authored-by: Jeppe Bundgaard <jb@truckwash.dk>
2026-08-04 16:08:31 +00:00
Jeppe BandJeppe Bundgaard 23fc410d25 xlvask-autopilot (#347)
Co-authored-by: Jeppe Bundgaard <jb@truckwash.dk>
2026-08-04 17:46:15 +02:00
Jeppe BandJeppe Bundgaard db9f589bf7 Resolve issue causing crash when plate_scanners.deleted_at was missing. (#346)
Co-authored-by: Jeppe Bundgaard <jb@truckwash.dk>
2026-08-04 16:43:46 +02:00
Jeppe B ab6c3ba5b6 Fix route permission instance calls (#344)
## Root cause

`route_t::hasPermission()` and `requirePermission()` are instance
methods. Route code was invoking them with `self::`; the new XL Vask
hall-scope helper made that call from a genuinely static context,
causing PHP to throw:

`Non-static method routes\\xlvaskUsageLogsRoute::hasPermission() cannot
be called statically`

## Changes

- Invoke route permission methods through `$this` across all 273
executable legacy calls in 45 route classes.
- Make `xlvaskUsageLogsRoute::allowedHallIdsForUser()` an instance
helper and update all 13 callers.
- Preserve the existing all-scope and own-scope hall selection rules.
- Add a token-aware regression test that rejects executable
`self::hasPermission()` and `self::requirePermission()` calls, while
ignoring comments.
- Add focused XL Vask tests for global scanner hall scope and
group-limited own scope.
- Update affected route contract assertions to the instance-call form.

## Verification

- PHP lint: all 53 changed PHP files
- Focused PHPStan: changed XL Vask route and both new regression tests —
clean
- Focused regression slice: 58 passed, 748 assertions
- Full local unit suite: 1,300 passed, 9,442 assertions (1 unrelated
existing warning, 1 environment skip)
- Full local API suite: 285 passed, 11,704 assertions
- Exact-SHA GitHub Tests workflow: all 7 jobs passed (unit, API,
integration, legacy, edge gateway, and supporting checks)
- Independent exact-SHA QA gate: PASS, no findings
- Independent exact-SHA security gate: PASS, no findings
- Independent exact-SHA reviewer gate: PASS, no findings
- Remote comparison: exactly one commit ahead of
`40b104abed7723a7d1b7028190ecda0e7aeef829`; all 53 remote blob hashes
matched the reviewed worktree

## Delivery state

Draft only for human review. No merge or deployment is included. Qodana
is skipped while the PR remains draft and is therefore not represented
as a passed gate.
2026-08-04 16:04:41 +02:00
Jeppe B 40b104abed Add governed XL Vask AI invoice automation (#343) 2026-08-04 11:32:49 +02:00
Jeppe B 842b06c93f Fix frontend release version recording (#342)
Add a scoped release-gate endpoint for exact frontend SHA recording and independent readback.
2026-08-03 16:11:09 +02:00
Jeppe B 1f03b46564 Fix XL-Vask usage metadata hydration (#341)
Keep automation metadata out of the strict legacy XL-Vask helper payload while returning it separately. This restores the invoice-period XL-Vask orders GET after the guarded autopilot deployment.
2026-08-03 16:00:52 +02:00
Jeppe B 6d888a455d Automate XL-Vask invoice-period resolution (#340)
Deploy the revision-aware XL-Vask import and guarded autopilot infrastructure. Automatic actions remain fail-closed pending production readiness, calibration, dry-run, and canary gates.
2026-08-03 15:33:55 +02:00
Jeppe B 0b304a2203 Gate invoice tree activation on audit schema (#339)
## Summary
- gate object-tree v2 on exact, schema-backed supersession audit columns
- serialize checked additive DDL and fail closed without disrupting
legacy invoicing
- preserve pre-schema supersession markers when structured columns are
still null
- add a superuser-only, self-scoped canary endpoint with locked
legacy-to-canonical allowlist migration
- verify effective activation, roll back failed readiness, and audit
enable/disable/failure

## Verification
- focused invoicing safety: 16 tests passed (99 assertions)
- full unit suite: 1,237 passed (9,003 assertions), 2 skipped, existing
warnings only
- PHP syntax and `git diff --check` clean
- independent architecture, security, and reviewer gates: GO

## Activation
Deploy with global database/environment enablement off. POST the
self-canary endpoint for one authenticated superuser, require
`configured_enabled=true` and `effective_enabled=true`, then verify the
exact period and tree GET routes. Roll back with the same endpoint using
`enabled=false`.
2026-08-03 13:03:55 +02:00
Jeppe B 068f9e254f Complete selected-customer invoice period tree (#338)
Add the authoritative revision-bound invoice collection tree and guarded cleanup, merge, price-reset, and transfer operations.
2026-08-03 12:02:34 +02:00
Jeppe B f37feef1e6 Fix customer login email session refresh (#337)
Invalidate cached auth sessions for every active customer token and return the persisted canonical login email.
2026-08-03 09:40:00 +02:00
Jeppe B c795df4aad Add invoice period review workflow (#336)
Improve the superuser invoice-period review API, stale-preview protection, queue visibility, review blockers, and e-conomic eligibility.
2026-08-02 19:20:50 +02:00
Jeppe B 1e0e051775 Harden Sæby demo registration and department scope (#335)
Complete and secure public customer/driver registration, authoritative limited-backoffice department scope, one-time employee QR login, and pricing concurrency for the Sæby demo.
2026-08-02 11:50:56 +02:00
Jeppe B 4587bdfb06 Restore API startup by isolating Bird activation (#334)
Keep Bird activation outside the PHP-FPM/nginx startup path so Bird configuration failures cannot make the core API unavailable. Retain guarded explicit activation and regression coverage.
2026-07-29 22:41:19 +02:00
Jeppe B a442e70744 Add secure Bird gateway for Pleno Control Plane (#332)
Add the Bird Control Plane gateway, signed webhook ingestion, policy-gated writes, fail-closed production auto-activation, and RSA-OAEP bootstrap credential flow.
2026-07-29 19:59:20 +02:00
Jeppe BandJeppe Bundgaard 1b161f1b96 Guard Superuser invoicing registration search fields (#331)
## Summary
- Adds a backend contract guard that Superuser invoicing period search
continues to include all separate registration columns: `reg_1`,
`reg_2`, and `reg_3`.
- This preserves the API/search behavior that the frontend object-tree
registration layout relies on.

## Tests
- `vendor/bin/pest
tests/Unit/Invoicing/InvoicingPeriodRouteGuardsTest.php --colors=always`

Note: local Composer install was run with
`--ignore-platform-req=ext-mysqli --ignore-platform-req=ext-gd` because
those PHP extensions are not enabled in this worker image; the executed
guard test does not use them.

Co-authored-by: Jeppe Bundgaard <jb@truckwash.dk>
2026-07-29 13:54:58 +02:00
Jeppe B 448e0e50c2 Fix api-v2 CORS at the edge without changing rollout flows (#330)
Add API-only Traefik CORS middleware labels while preserving configured origins and the existing rollout/load-balancer behavior.
2026-07-29 09:01:08 +02:00
Jeppe B 710baad28e Add one-time limited backoffice login grants (#329)
## Summary

Adds the missing backend contract used by Pleno Control Plane
Conversations/Suggestions to create an employee login action safely.

- issues 60–900 second one-time limited-backoffice login grants
- persists only SHA-256 bearer digests; bearer recovery is deterministic
under the server encryption key for identical idempotent retries
- enforces manager permissions, department scope, active
managed-employee constraints, one-time atomic exchange, revocation,
expiry, and account-deletion cleanup
- adds employee-create idempotency so an approved automation retry
cannot duplicate an employee
- documents the create, revoke, and unauthenticated exchange endpoints
in OpenAPI

## Security and concurrency

- bearer values are returned only in a URL fragment and are never
written to logs or database plaintext
- employee and grant rows use a consistent employee-then-grant lock
order
- deactivation revokes outstanding grants and existing sessions in the
same transaction
- consumed, revoked, expired, or payload-mismatched idempotent replays
fail closed

## Verification

- `scripts/php-ci-test.sh api`: 273 passed, 11,086 assertions (one
inherited warning)
- focused security contract: 1 passed, 21 assertions
- PHP syntax checks passed for the service and routes
- `git diff --check` passed

## Dependency

Required by copenhagentruckwash/pleno-control-plane#1. Merge before the
matching frontend and Control Plane PRs.
2026-07-29 00:01:13 +02:00
Jeppe BandJeppe Bundgaard 42ddce84bc Serialize VAT collection mutations with payment operations (#326)
## Summary

- Makes Stripe Terminal card payment intents always use 25% moms in the
API, independent of any client-supplied `tax_percentage`.
- Updates amount calculation, metadata persistence, stored-intent reuse
matching, the authoritative OpenAPI contracts, and operation-specific
Writerside outputs.
- Prevents double charging and false order closure across stale,
concurrently succeeded, partially recorded, or mismatched intents.
- Serializes payment create/capture/closure with order-item changes and
every order-to-invoice-collection reassignment through shared database
locks.
- Converts expected lock contention and reconciliation cases into
deliberate 409 responses.

## Exact-head evidence

Current head: `3a0f70d315a94d2efe586a2188d2c54f8ff11cd4`

- PHP syntax passed for all changed runtime files.
- Focused Orders suite: **42 tests / 293 assertions passed**.
- `git diff --check` passed.
- Fresh exact-head Tests and Qodana are running.
- Every Codex finding has a concrete reply; a fresh exact-head review is
requested below.

## Safety behavior

- Caller-controlled VAT is absent from request contracts; fixed 25% moms
is server-owned.
- A succeeded payment is preserved, requires the full expected
`amount_received`, and cannot close a changed/mismatched or
already-claimed collection.
- A compatible partially recorded Stripe closure is completed
idempotently; conflicting partial state fails closed for manual
reconciliation.
- Every cancellation/delete caller honors a concurrent-success result
and never falsely reports a completed payment as cleared.
- Price changes and invoice-collection reassignment share the payment
lock through validation, capture, post-capture reload, and closure.
- Reader changes are persisted only for reusable matching intents, so
stale intent cancellation targets the original terminal.
- Accepted legacy succeeded intents normalize stored tax to 25% before
response construction.

---------

Co-authored-by: Jeppe Bundgaard <jb@truckwash.dk>
2026-07-28 22:00:59 +02:00
Jeppe B da0113e3ed Auto-disable department self-serve at opening (#323)
Auto-disable department self-serve at opening
2026-07-28 18:03:05 +02:00
Jeppe BandJeppe Bundgaard 3c13892366 Harden subuser permission payloads (#328)
## Summary
- Normalize subuser grant permission payload keys before enum validation
so mixed-case customer-facing writes are accepted and deduped
consistently.
- Add a focused subuser route static check for permission payload
normalization.

## Verification
- `php
services/nginx/app/tests/subusers/SubusersRoutePermissionLinkTest.php &&
php
services/nginx/app/tests/subusers/SubusersRoutePermissionsPayloadTest.php`
- `php -l services/nginx/app/routes/subusersRoute.php && php -l
services/nginx/app/tests/subusers/SubusersRoutePermissionsPayloadTest.php`

---------

Co-authored-by: Jeppe Bundgaard <jb@truckwash.dk>
2026-07-28 17:06:29 +02:00
Jeppe B 30d860fdef Retire payment links and capture card payments (#327)
## Summary

- retire Stripe hosted payment-link creation routes used by POS and
order management
- automatically capture authorized payment intents rather than requiring
a separate manual capture action
- preserve ordinary terminal payment and payment-intent lifecycle
behavior
- add API and wiring regressions for payment-link retirement and
automatic capture

Paired frontend change:
https://github.com/copenhagentruckwash/pleno-vue/pull/233

## Verification

- focused backend unit suite: 2 tests, 36 assertions passed
- PHP syntax checks passed
- paired frontend unit and Playwright suites passed locally
- full required GitHub runner suites are required before this task may
enter Review or merge

## Security and operational notes

- no credentials, terminal secrets, or payment data are added
- no live Stripe account or physical terminal was exercised locally
- automatic merge remains gated on both paired PRs having passing
required checks and current branches
2026-07-27 19:09:37 +02:00
Jeppe BandJeppe Bundgaard d3e4798b11 Complete subuser notification and recovery lifecycle (#325)
## Summary

- notify customers by SMS with approve/deny links when a subuser
requests access
- notify subusers by SMS after approval or denial, including manual
grant changes
- support subuser password reset and authenticated password changes
- add read-only token previews followed by explicit POST confirmation
- store short-lived one-time purpose-bound action tokens only as SHA-256
digests
- serialize grant decisions transactionally to prevent conflicting
concurrent actions
- document the API contract in OpenAPI

## Security

- generic reset responses reduce account enumeration
- URL tokens are removed from browser history after frontend bootstrap
- approval previews never mutate state
- concurrent decisions lock the exact grant row
- SMS failures remain non-fatal and are returned as delivery status

Residual risk: existing subuser sessions cannot all be centrally
invalidated after password reset because there is no per-subuser session
index; they expire normally within the existing session lifetime.

## Verification

- backend Pest: 14 tests, 91 assertions
- PHP syntax checks passed
- focused PHPStan passed
- OpenAPI YAML parsed successfully
- `git diff --check` passed

Database-backed API integration tests were unavailable because the local
environment lacks the required database configuration.

## Paired delivery

Paired Frontend PR:
https://github.com/copenhagentruckwash/pleno-vue/pull/231

Both PRs are required before completion. The frontend PR contains the
responsive visual comparisons.

Co-authored-by: Jeppe Bundgaard <jb@truckwash.dk>
2026-07-27 18:40:40 +02:00
Jeppe B 8d8f0eccce Fix cron worker minute cadence (#324)
Replace drifting cron loops with persistent cadence-tracked workers and CI-verifiable Compose wiring.
2026-07-27 18:16:09 +02:00
Jeppe B 6e24718c1f Isolate backend Docker jobs on ephemeral runners (#322)
## Summary
- Run Docker-producing PHP, Edge Broker, and Edge Gateway jobs on
ephemeral Ubuntu workspaces for both PR and push events.
- Keep the non-Docker Edge Agent and Release Manager gate on the trusted
backend pool.
- Preserve the explicit system-socket selection and fail-closed Docker
access check.

## Root cause
Exact-master run 29942825210 got past Docker access, then later jobs
failed during checkout because an earlier container left
`services/php/logs/error.log` root-owned in the reused self-hosted
workspace. This is workspace contamination, not a product-test failure.
The shallow frontend repair pattern would miss the depth-4 file and
would accumulate undeletable trash directories.

## Verification
- Workflow YAML parse passed.
- `git diff --check` passed.
- PR CI must be green; after merge, exact-master Required CI and the
non-skipped Release Manager gate are mandatory.
2026-07-22 19:47:31 +02:00
Jeppe B 9b481e0957 Use the system Docker socket in backend CI (#321)
## Summary
- Keep untrusted PRs on ephemeral Ubuntu runners and trusted pushes on
the local backend pool.
- Force Docker-dependent jobs to the working system socket instead of
the unavailable default rootless context.
- Preserve the fail-closed Docker access check and never chmod the
socket.

## Evidence
- Exact master run 29942048689 failed before tests because plain Docker
commands resolved to `/run/user/1000/docker.sock`.
- Backend listener processes already have docker-group membership;
`/var/run/docker.sock` is root:docker 0660.
- `DOCKER_HOST=unix:///var/run/docker.sock docker version` succeeds
locally with server 29.3.1.
- Workflow YAML parse and `git diff --check` pass.

Exact-master Required CI and Release Manager gate success remain
mandatory after merge.
2026-07-22 19:33:13 +02:00
Jeppe B 0060fb45ca Add in-app account deletion (#319)
## Summary
- Add self-service deletion for the authenticated customer or subuser
identity only.
- Preserve shared customer grants, reset keys, bookings, order bookings,
vehicles, invoices, and legally required history.
- Require password/TOTP or a fresh deletion-specific, five-minute,
single-use WebAuthn assertion.
- Reject support impersonation and expired legacy plain-session tokens.
- Use durable database throttling, transactional request processing, a
durable outbox, and terminal `manual_review` state.
- Keep API and worker default-off behind separate
`account_deletion.api_enabled` and `account_deletion.worker_enabled`
module-config flags.

## Safe rollout
1. Keep both flags disabled.
2. Run `php scripts/account-deletion-schema.php check`.
3. If needed, run `php scripts/account-deletion-schema.php apply --yes`,
then rerun `check` until `ready:true`.
4. Deploy the frontend companion PR while the API remains disabled.
5. Enable `api_enabled` for a controlled canary; verify password and
passwordless request flows plus immediate authentication revocation.
6. Inspect queued request/outbox state, then enable `worker_enabled`.
7. Verify anonymization, preserved tenant/history data, outbox delivery,
retries, and manual-review behavior before broad rollout.

## Verification
- Account deletion unit tests: 2 passed, 43 assertions.
- PHP lint, both OpenAPI YAML parses, runtime-DDL scan,
destructive-scope scan, and `git diff --check` passed.
- Full API/unit/integration evidence is required from exact-head CI;
local Docker is unavailable and shared-vendor tests were explicitly
discarded.

## Security notes
- Schema mutation is CLI-only; web and cron paths perform read-only
readiness checks.
- Runtime behavior fails closed when schema/config/throttle/delivery
prerequisites are unavailable.
2026-07-22 19:22:17 +02:00
Jeppe B 34cf804d75 Harden CI runner and release gate security (#320)
## Summary

- run untrusted pull-request jobs on ephemeral `ubuntu-24.04` runners
- reserve the local backend runner pool for trusted branch pushes
- remove world-writable Docker-socket fallbacks
- pin core GitHub Actions and disable checkout credential persistence
- remove the release-manager PHP parse-error fail-open path

## Why

Pull-request code previously ran on persistent self-hosted runners with
Docker access, and CI contained permission weakening and a release-gate
break-glass success path. Those behaviors were unsafe for autonomous
intake.

## Validation

- workflow YAML parsed
- backend AI workflow outputs are in sync
- pinned action SHAs match the current v4 tags
- `git diff --check`

## Risk and activation

This is an R4 CI/release-policy change. Keep the PR draft for human
review and let required CI prove the hosted-runner path before merge.
2026-07-22 19:05:55 +02:00
Jeppe B 677d4700b0 Enforce customer product restrictions for order bookings (#318)
## What changed

- validate every normalized order-booking item against active customer
product rules before reservation and persistence
- return a structured HTTP 400 response containing the rejected product
and matching rule metadata
- document the rejection response in both OpenAPI specifications
- add API coverage for restricted base products, restricted add-ons, and
allowed neighboring products

## Why

Frontend rule guidance alone cannot prevent stale or crafted requests
from persisting restricted booking products. The booking write boundary
must enforce the same customer rules.

## Validation

- full backend API suite
- focused order-booking API coverage
- PHP syntax checks
- OpenAPI and diff checks

## Related frontend PR

The coordinated frontend PR provides fail-closed selection, recovery,
and responsive booking-page behavior.
2026-07-20 14:09:20 +02:00
Jeppe B abde54c898 Allow Capacitor iOS API origin (#317)
Allow the exact Capacitor iOS WebView origin through credentialed CORS while strictly validating request-origin syntax.
2026-07-20 13:53:55 +02:00
Jeppe B b177347bf5 Ignore PHPUnit result cache (#316)
Ignore the generated PHPUnit cache directory and remove its volatile test-results file from Git tracking so test runs no longer dirty protected branch checkouts.
2026-07-20 08:49:27 +02:00
Jeppe B 32a5b99204 Resolve remaining backend full-scan Qodana findings (#315)
Fix the two High findings exposed by the first full master Qodana scan after the broader remediation.
2026-07-17 06:24:47 +02:00
Jeppe B 2a6a86c9c3 Resolve backend Qodana critical and high findings (#314)
Resolve recommended-profile Critical and High findings, retain narrow analyzer exceptions, and update the edge-broker WebSocket dependency to a non-vulnerable release.
2026-07-17 05:44:16 +02:00
Jeppe B 6566027746 Configure advisory Qodana analysis (#313)
## Summary

- configure advisory Qodana PHP 2026.1 analysis for trusted pull
requests and master, beta, canary, and internal branch scans
- install both Composer projects and the edge-agent/edge-broker Node
dependencies before analysis
- exclude generated, vendor, build, cache, legacy-test, and local-agent
trees
- keep Quick Fixes, SARIF artifacts, baselines, thresholds, and
required-check enforcement disabled during calibration

## Safety

- fails closed when QODANA_TOKEN is absent
- skips draft, fork, and Dependabot pull requests
- uses least-privilege GitHub permissions and immutable action SHAs
- uploads findings to the dedicated api Qodana Cloud project

## Validation

- actionlint 1.7.12
- SchemaStore qodana-1.0 validation
- bootstrap shell syntax and lockfile structure checks
- immutable action tag verification
- git diff --check
- independent review completed with no findings

## Live verification

- [PR-mode
scan](https://github.com/copenhagentruckwash/api/actions/runs/29494056175)
completed successfully with 0 changed-file problems, 439 inspections,
and a passed license audit ([Qodana
report](https://qodana.cloud/projects/P2nXd/reports/LJv98e))
- [full branch
scan](https://github.com/copenhagentruckwash/api/actions/runs/29495399119)
completed successfully and uploaded 8,248 current findings across 725
files, 439 inspections, and a passed license audit to the dedicated api
project ([Qodana
report](https://qodana.cloud/projects/P2nXd/reports/qJMOxX))
- the initial debt remains advisory; baseline and required-check
enforcement are intentionally deferred until findings are triaged
2026-07-16 14:35:59 +02:00
Jeppe B 511605b619 Verify API master branch protection (#312)
Record the live ruleset and complete the protected-path canary.
2026-07-16 12:55:18 +02:00
Jeppe B c2abf17cd7 Prepare API default branch protection (#311)
Add a stable Required CI gate, branch-protection desired state, and publishing runbook.
2026-07-16 12:38:01 +02:00
Jeppe B fefe18a719 Fix legacy customer attribute session query 2026-07-16 12:20:20 +02:00
Jeppe B 9b2d5d5291 Fix customer restriction CI regressions 2026-07-16 12:06:42 +02:00
Jeppe B e1fb79d9b6 Add customer rule product restrictions 2026-07-16 11:50:52 +02:00
Jeppe B 879dfcf79a Improve invoice period data and POS add-on validation 2026-07-15 17:04:52 +02:00
Jeppe B 0feb705059 Support collected invoice economic PDF downloads 2026-07-14 15:39:39 +02:00
Jeppe Bundgaard 69b3bf83c4 Allow custom subuser grant permission writes 2026-07-13 22:30:18 +02:00
Jeppe Bundgaard 5bac316e4b Add loading screen for session confirmation and enhance vehicle management labels 2026-07-13 22:11:54 +02:00
Jeppe Bundgaard 233133365d Guard chauffeur vehicles on legacy schemas 2026-07-13 20:03:50 +02:00
Jeppe Bundgaard 026492c3bd Queue cron runs through workers 2026-07-13 20:03:39 +02:00
Jeppe Bundgaard 403f93e62c Fix self-serve schema compatibility in CI 2026-07-13 15:36:03 +02:00
Jeppe Bundgaard 582edd3e6c Implement subuser verification and invoice/self-serve API fixes 2026-07-13 15:11:49 +02:00
Jeppe Bundgaard a4fafaf7fb Enhance subuser management functionality and improve UI responsiveness 2026-07-13 10:25:26 +02:00
Jeppe Bundgaard 327e9cf817 Update self-serve permissions and enhance UI components for customer interactions 2026-07-13 10:21:22 +02:00
Jeppe Bundgaard 012e5366ba Add system status displays for Minio and Redis, and enhance backup configuration 2026-07-13 10:08:00 +02:00
Jeppe B fa1ade555f Fix self-serve cron registry test
Fix self-serve cron registry test
2026-07-09 11:36:11 +02:00
Jeppe B 7a1c444df0 Activate self-serve opening relays
Activate self-serve opening relays
2026-07-09 11:27:32 +02:00
Jeppe Bundgaard 6a00f023b1 Refactor cron scheduling 2026-07-09 11:04:13 +02:00
Jeppe B 8aefbd8fb3 Guard wash subscription distribution SQL
Guard the wash subscription distribution query after invoice-inclusion filtering removes all candidate orders, preventing an empty IN () clause on the invoicing distribution endpoint.

Verified with focused syntax, Pest, PHPStan, and invoicing unit-suite checks.
2026-07-09 10:24:02 +02:00
Jeppe B a7181a4ab2 Fix edge gateway relay binding reactivation
Reactivate existing relay binding rows when a gateway/relay pair is re-added after soft deletion, avoiding duplicate uniq_edge_gateway_binding inserts. Add regression coverage for the reactivation path.
2026-07-08 18:46:33 +02:00
Jeppe Bundgaard fc6c76ad1b Add customer rule cleanup for spotfree addon products with preview and apply functionality 2026-07-08 14:40:46 +02:00
Jeppe Bundgaard 6a694f92cc Add bulk action preview and apply endpoints for collected invoices 2026-07-08 13:46:31 +02:00
Jeppe Bundgaard b7a2dc04d7 Merge remote-tracking branch 'origin/master' 2026-07-08 13:03:49 +02:00
Jeppe Bundgaard 870b88e707 Add assertError method to ApiResponse and enhance CI test failure handling 2026-07-08 13:03:33 +02:00
Jeppe B e14cddc1fb Fix API suite regressions 2026-07-08 12:54:26 +02:00
Jeppe B 31887fa8c9 Cover API CI skip prevention wiring 2026-07-08 12:32:27 +02:00
Jeppe B eac83b18a0 Preflight required extensions for API CI 2026-07-08 12:32:19 +02:00
Jeppe B 3817a37021 Make API CI fail on skipped bootstrap 2026-07-08 12:31:40 +02:00
Jeppe Bundgaard 940a3e5e9b Refactor superuser grant selection logic to prioritize the most recently updated grant 2026-07-08 12:16:22 +02:00
Jeppe Bundgaard 3221223865 Enhance vehicle summary retrieval for customers and update OpenAPI schema for subuser management grants 2026-07-08 12:15:18 +02:00
Jeppe Bundgaard b51006d9d1 Refactor subuser management payload and enhance grant deduplication logic 2026-07-08 12:13:12 +02:00
Jeppe Bundgaard 6b7592921d Add subuser permission templates service and related tests 2026-07-08 11:49:40 +02:00
Jeppe Bundgaard f26a427510 Add test for customer booking sessions to retrieve single-product final pricing with context 2026-07-08 10:41:56 +02:00
Jeppe Bundgaard 6de747252f Add department pricing normalization for order bookings 2026-07-08 10:35:59 +02:00
Jeppe Bundgaard ff225ff5e7 Refactor getTargetItems method to include customer and department parameters for improved item normalization 2026-07-08 10:35:39 +02:00
Jeppe Bundgaard dcef993f12 Implement department wash count service and refactor related reporting functions 2026-07-08 10:35:24 +02:00
Jeppe Bundgaard 23aca449f7 Add department wash count service for tracking wash counts and transaction summaries 2026-07-08 10:25:03 +02:00
Jeppe Bundgaard 31b5ba136a Add user-scoped routes for managing subusers and their grants 2026-07-08 10:24:50 +02:00
Jeppe Bundgaard f0b5479f30 Enhance department pricing functionality and improve related tests 2026-07-08 09:35:40 +02:00
Jeppe B b77efc538a Fix backend test gates and department product access 2026-07-07 22:16:37 +02:00
Jeppe Bundgaard 10d1eb5bac Refactor system search cache handling and update OpenAPI specifications for max results 2026-07-07 18:59:07 +02:00
Jeppe Bundgaard 084435e9b8 Add customer pricing permissions and update related tests 2026-07-07 17:58:39 +02:00
Jeppe Bundgaard 172a21c517 Implement department-specific customer pricing functionality 2026-07-07 17:27:56 +02:00
Jeppe Bundgaard c24428e4c7 Update ProductsApiTest to use 'user' session for department pricing tests 2026-07-07 14:38:41 +02:00
Jeppe Bundgaard bf1d6a583e Allow customers to read own attributes 2026-07-07 13:05:24 +02:00
Jeppe Bundgaard 08ac16e665 Fix customer wash certificate access and emails 2026-07-07 12:37:19 +02:00
Jeppe Bundgaard 79185a3c76 Enhance product listing for customer booking sessions: allow access to booking-visible products without requiring additional permissions 2026-07-07 12:23:56 +02:00
Jeppe Bundgaard 3a730e3507 Merge limited backoffice role permission templates 2026-07-07 03:27:39 +02:00
Jeppe Bundgaard ce43c4e064 Expose limited backoffice role permission templates 2026-07-07 03:23:42 +02:00
Jeppe B 579ddcf510 Merge pull request #307 from copenhagentruckwash/copilot/update-limited-backoffice-roles
Fix limited-backoffice role permissions and enforce department access on order mutations
2026-07-07 02:53:06 +02:00
Jeppe Bundgaard 0b342a7780 Align limited backoffice permission cap tests 2026-07-07 02:47:49 +02:00
copilot-swe-agent[bot] 57bcbaf72a Fix 11 failing API tests across 4 files 2026-07-07 00:31:43 +00:00
copilot-swe-agent[bot] d9fbba3130 Return 404 when order item not found in DELETE /order/items 2026-07-06 23:19:00 +00:00
copilot-swe-agent[bot] e4465d9d91 Improve DELETE /order/items: clearer error message, 404 when order not found 2026-07-06 23:17:50 +00:00
copilot-swe-agent[bot] 734cd13c87 Handle prepared statement failure with error response in DELETE /order/items 2026-07-06 23:16:55 +00:00
copilot-swe-agent[bot] d0f94ac549 Use prepared statements for all new DB queries in tests and route 2026-07-06 23:15:59 +00:00
copilot-swe-agent[bot] 1d25cbe21c Fix SQL injection concerns: use prepared statements in orderItemsRoute and tests 2026-07-06 23:14:34 +00:00
copilot-swe-agent[bot] 53d0636193 Fix limited-backoffice permissions and add department access restrictions 2026-07-06 23:12:01 +00:00
Jeppe B 04bb26f1b0 Merge pull request #306 from copenhagentruckwash/copilot/fix-php-api-job-failure
Fix two failing LimitedBackofficeApiTest assertions
2026-07-07 00:44:53 +02:00
copilot-swe-agent[bot] df0d4783d0 Remove search_customers and search_vehicles from raw-permission exclusion list 2026-07-06 22:37:40 +00:00
copilot-swe-agent[bot] 39c06ceab6 Address code review: restore filter passthrough and add clarifying comments 2026-07-06 22:28:45 +00:00
copilot-swe-agent[bot] 7dd428d18e Fix two failing LimitedBackofficeApiTest tests
Fix 1: Remove add_order_attachments and download_order_attachments from
the raw-permissions exclusion check in the test. These strings are valid
capability names that legitimately appear in the /limited-backoffice/roles
response, so including them in the 'should not contain' list caused a
false failure.

Fix 2: Update limitedBackofficeEmployeeListMode() in usersRoute.php to
exclude active limited backoffice employees when include_limited_backoffice_employees
is not set and the customer_number:0 filter is in use. Previously the
method returned additional_where:null in this case, so limited employees
were included in the result set alongside regular backoffice employees.
2026-07-06 22:27:45 +00:00
copilot-swe-agent[bot] 0103a40156 Initial plan 2026-07-06 22:19:38 +00:00
Jeppe B e208b1b2a4 Merge pull request #305 from copenhagentruckwash/codex/limited-backoffice-employee-migration
Add limited backoffice employee migration
2026-07-07 00:14:25 +02:00
Jeppe Bundgaard 6b4b55cb62 Add limited backoffice employee migration 2026-07-07 00:10:22 +02:00
Jeppe Bundgaard 0cca597fdc Fix XLVask usage import dates
Fix XLVask usage-log import metadata and period-scoped Selvvask automation.
2026-07-06 23:49:28 +02:00
Jeppe B 709c6acbba Fix product null department permissions
Treats null-like optional product query params as omitted and avoids department_access_0 permission checks.
2026-07-06 20:14:45 +02:00
Jeppe Bundgaard ed2736e528 Fix product null department permissions 2026-07-06 19:56:07 +02:00
Jeppe B c7f5c73a9e Merge pull request #303 from copenhagentruckwash/codex/daily-report-product-targets-api
[codex] Add daily report product target API
2026-07-06 19:35:37 +02:00
Jeppe Bundgaard c10af48954 Add daily report product target API 2026-07-06 18:52:24 +02:00
Jeppe Bundgaard 7ac5c5585b Add limited backoffice employee QR login links 2026-07-06 17:32:58 +02:00
Jeppe B 8544ce0a18 Merge pull request #297 from copenhagentruckwash/codex/customer-product-fixed-price-overrides
Add customer product fixed price overrides
2026-07-06 17:23:06 +02:00
Jeppe Bundgaard 614715822f Fix backend merge fallout for booking and limited employees 2026-07-06 17:16:22 +02:00
Jeppe Bundgaard 1da02e2486 Fix limited backoffice price save reset 2026-07-06 17:13:04 +02:00
Jeppe B 742b15116d Merge pull request #295 from copenhagentruckwash/fix/economic-ean-transfer
Fix e-conomic EAN customer transfer
2026-07-06 17:00:57 +02:00
Jeppe Bundgaard e0ae74bdc2 Merge remote-tracking branch 'origin/master' into codex/customer-product-fixed-price-overrides 2026-07-06 17:00:52 +02:00
Jeppe Bundgaard 08dc803b3e Make limited backoffice employees regular employees 2026-07-06 16:59:32 +02:00
Jeppe Bundgaard 248a901f24 Merge master into fixed price override branch 2026-07-06 16:54:01 +02:00
Jeppe Bundgaard 8bbdf9daf5 Require booking add node for subuser booking creation 2026-07-06 16:48:07 +02:00
Jeppe B c089186046 Merge pull request #302 from copenhagentruckwash/codex/customer-orderbooking-create-without-permission
Allow customer order booking creation without booking permission
2026-07-06 16:39:17 +02:00
Jeppe Bundgaard 2ae1fc3fcf Allow customer order booking creation without booking permission 2026-07-06 16:33:31 +02:00
Jeppe Bundgaard d9eacf6f84 Deduplicate limited backoffice price products 2026-07-06 16:28:08 +02:00
Jeppe B f262047476 Merge pull request #300 from copenhagentruckwash/codex/scoped-monthly-split-api
Scope monthly invoice split API
2026-07-06 16:01:54 +02:00
Jeppe B b8390ac0d3 Merge pull request #298 from copenhagentruckwash/codex/only-tankcleaning-order-enforcement
Enforce only tankcleaning order products
2026-07-06 16:01:40 +02:00
Jeppe B 0d4a5470e5 Add superuser department overview API (#301)
Merge backend API for the superuser department overview.
2026-07-06 16:01:04 +02:00
Jeppe B 845ca6e48e Merge pull request #290 from copenhagentruckwash/codex/custom-pricing-only-departments
Add custom-only department pricing enforcement
2026-07-06 15:31:27 +02:00
Jeppe Bundgaard 1cda2a81aa Merge remote-tracking branch 'origin/master' into codex/custom-pricing-only-departments
# Conflicts:
#	services/nginx/app/tests/Api/OrderItemsApiTest.php
2026-07-06 15:21:31 +02:00
Jeppe BandJeppe Bundgaard 8e46ce1b04 [codex] Allow error reports without screenshots (#299)
* Allow error reports without screenshots

* Stabilize edge gateway shell transcript smoke

---------

Co-authored-by: Jeppe Bundgaard <jb@truckwash.dk>
2026-07-06 14:27:47 +02:00
Jeppe Bundgaard 9f797bf6b8 Add opening hours table to API test schema 2026-07-06 14:27:39 +02:00
Jeppe Bundgaard d345db927f Add daily report table to API test schema 2026-07-06 14:16:41 +02:00
Jeppe BandJeppe Bundgaard 11c2a1b72e Block restricted customer order items (#296)
Co-authored-by: Jeppe Bundgaard <jb@truckwash.dk>
2026-07-06 14:06:29 +02:00
Jeppe Bundgaard eca7a81f9d Add superuser department overview API 2026-07-06 13:59:40 +02:00
Jeppe Bundgaard 62f2c80dda Scope monthly invoice split endpoint 2026-07-06 13:37:31 +02:00
Jeppe BandJeppe Bundgaard 6f3d7e0f7d Add limited backoffice employee contact fields (#294)
Co-authored-by: Jeppe Bundgaard <jb@truckwash.dk>
2026-07-06 13:14:06 +02:00
Jeppe Bundgaard 430c90cbca Enforce only tankcleaning order products 2026-07-06 12:53:42 +02:00
Jeppe Bundgaard f02dfd8c9c Add customer product fixed price overrides 2026-07-06 12:52:33 +02:00
Jeppe B a8fba73d99 Add limited backoffice role permission details (#291)
Adds grouped safe permission metadata for limited backoffice role presets.
2026-07-06 12:24:54 +02:00
Jeppe B 669759461d Merge pull request #293 from copenhagentruckwash/codex/economic-collected-invoice-transfer-speed
Optimize collected e-conomic invoice transfers
2026-07-06 11:49:47 +02:00
Jeppe Bundgaard db1b9a2c96 Fix e-conomic EAN customer transfer 2026-07-06 11:34:23 +02:00
Jeppe Bundgaard 38814545c4 Optimize collected e-conomic invoice transfers 2026-07-06 11:31:22 +02:00
Jeppe B 94c3654240 Merge pull request #292 from copenhagentruckwash/fix/limited-backoffice-price-save
[codex] Fix limited backoffice price saves on legacy schema
2026-07-06 11:03:44 +02:00
Jeppe Bundgaard 9fa249cc11 Fix limited backoffice price saves on legacy schema 2026-07-06 10:53:18 +02:00
Jeppe Bundgaard 215c8d0fbb Add limited backoffice role permission details 2026-07-06 10:38:51 +02:00
Jeppe Bundgaard 84dec4c0a2 Stabilize custom pricing API fixture 2026-07-06 10:34:57 +02:00
Jeppe Bundgaard d47ea1d659 Add custom-only department pricing enforcement 2026-07-06 10:15:11 +02:00
Jeppe BandJeppe Bundgaard 3f41eebdf6 Default vehicle subscriptions to false (#288)
Co-authored-by: Jeppe Bundgaard <jb@truckwash.dk>
2026-07-06 09:21:31 +02:00
Jeppe B 64e0b2444b Merge pull request #289 from copenhagentruckwash/fix/limited-backoffice-api-compat
[codex] Fix limited backoffice schema compatibility
2026-07-06 09:20:56 +02:00
Jeppe Bundgaard 18fede78f8 Fix limited backoffice schema compatibility 2026-07-06 09:15:55 +02:00
Jeppe Bundgaard 243d68ab59 Fix edge gateway relay command draining 2026-07-02 12:41:34 +02:00
Jeppe Bundgaard 62c1393f62 Refactor product data handling and improve error management in superuser products layout 2026-07-02 11:48:21 +02:00
Jeppe B f0a8299133 Merge pull request #287 from copenhagentruckwash/fix/api-ci-limited-backoffice
Fix API CI failures
2026-07-02 11:03:12 +02:00
Jeppe Bundgaard e36f6da926 Run Qodana on backend runner pool 2026-07-02 10:57:47 +02:00
Jeppe Bundgaard 4a8c2a9fd9 Avoid npm cache hang in edge CI 2026-07-02 10:50:25 +02:00
Jeppe Bundgaard 248b2e4eca Fix API CI failures 2026-07-02 10:42:13 +02:00
Jeppe Bundgaard 1d1ebd2176 Add limited backoffice functionality with employee management and department pricing 2026-07-01 16:37:32 +02:00
Jeppe Bundgaard 4252f9a42b Stabilize edge gateway API CI 2026-07-01 14:01:50 +02:00
Jeppe Bundgaard 4fdeedab45 Handle invalid edge installer tokens 2026-07-01 13:41:20 +02:00
Jeppe Bundgaard 866a5be126 Refactor subuser permissions and enhance artifact management 2026-07-01 13:18:27 +02:00
Jeppe Bundgaard 11d39af934 Normalize edge broker URL updates 2026-07-01 11:53:01 +02:00
Jeppe Bundgaard f706531534 Stabilize gateway E2E broker config 2026-07-01 11:44:36 +02:00
Jeppe Bundgaard a839eac4c1 Allow HTTP operation completion in gateway E2E 2026-07-01 11:34:39 +02:00
Jeppe Bundgaard 581d28e9ce Stabilize edge gateway CI assertions 2026-07-01 11:25:01 +02:00
Jeppe Bundgaard 2ba39b8174 Fix edge gateway CI broker path 2026-07-01 11:17:01 +02:00
Jeppe Bundgaard 6af55a44c9 Fix API CI broker and transport fixtures 2026-07-01 11:09:38 +02:00
Jeppe Bundgaard 24badc39d7 Keep broker path in API CI public URL 2026-07-01 11:02:26 +02:00
Jeppe Bundgaard f5e0baaab6 Use explicit Docker subnets in API CI 2026-07-01 11:00:13 +02:00
Jeppe Bundgaard 178c84ba60 Use direct broker port for API CI smoke 2026-07-01 10:57:39 +02:00
Jeppe Bundgaard 713d40a876 Prune stale Docker networks in API CI 2026-07-01 10:53:25 +02:00
Jeppe Bundgaard 9db1964038 Fix edge agent CI setup 2026-07-01 10:51:14 +02:00
Jeppe Bundgaard 1ca42055b0 Use docker-capable API runners 2026-07-01 10:42:30 +02:00
Jeppe Bundgaard c04bda7368 Normalize API runner Docker access 2026-07-01 10:38:49 +02:00
Jeppe Bundgaard 1f47843699 Fix edge expected state CI coverage 2026-07-01 10:33:56 +02:00
Jeppe Bundgaard dca738db82 Resolve edge schema DB config from env 2026-07-01 10:28:40 +02:00
Jeppe Bundgaard 57f364ad0f Use backend runners for API tests 2026-07-01 10:21:08 +02:00
Jeppe Bundgaard a826153bb5 Use namespaced DB helper in schema bootstrap 2026-07-01 10:04:28 +02:00
Jeppe Bundgaard 72bd22a707 Load DB helper for edge gateway schema bootstrap 2026-07-01 09:57:34 +02:00
Jeppe Bundgaard cc73d80dbc Use PDO in edge gateway schema bootstrap 2026-07-01 09:46:35 +02:00
Jeppe Bundgaard f0a5b15442 Add edge agent expected relay state API 2026-07-01 09:40:54 +02:00
Jeppe Bundgaard eefe5630f4 Honor staged edge gateway update windows 2026-06-30 17:23:04 +02:00
Jeppe Bundgaard b0ea771e6a Add handling for self-serve machine signals in edge agent 2026-06-30 16:19:43 +02:00
Jeppe Bundgaard eb21405a3d Add outbox replay handling with configurable limits and timeouts 2026-06-30 16:16:08 +02:00
Jeppe Bundgaard 8ea10ef808 Implement relay toggle handling with configurable timers and device generation resolution 2026-06-30 15:53:29 +02:00
Jeppe Bundgaard ac7da807bd Route local lane gate opens through edge bindings 2026-06-30 13:52:12 +02:00
Jeppe Bundgaard 3eafc597c6 Use edge bindings for lane hardware batch relays 2026-06-30 13:28:03 +02:00
Jeppe Bundgaard f7a4126718 Fix router matching for underscored route params 2026-06-30 13:10:06 +02:00
Jeppe Bundgaard c6cf953ede Fix relay batch gateway binding resolution 2026-06-30 12:52:53 +02:00
Jeppe Bundgaard d902202fe9 Fallback relay commands after broker dispatch failures 2026-06-30 12:32:10 +02:00
Jeppe Bundgaard acc80920f9 Preserve LAN worker auth after agent config reload 2026-06-30 11:39:19 +02:00
Jeppe Bundgaard 53246af629 Update EdgeGatewayManagerUrlTest to improve installation phase checks and remove obsolete heartbeat expectations 2026-06-30 11:18:00 +02:00
Jeppe Bundgaard bd87e94472 Normalize line endings in install scripts and enhance service stop commands for robustness 2026-06-30 11:06:33 +02:00
Jeppe Bundgaard 7ccbb68ffa Refactor Dockerfiles to streamline PHP extension checks and remove unnecessary installations 2026-06-30 10:58:08 +02:00
Jeppe Bundgaard 4a7fc7c534 Refactor Dockerfiles to conditionally install curl extension if not already present 2026-06-30 10:35:30 +02:00
Jeppe Bundgaard f4343ae114 Enhance stopping wash functionality with local storage management and restoration logic 2026-06-30 10:16:13 +02:00
Jeppe Bundgaard 0da02dfeb5 Add batch processing for lane hardware commands and status retrieval 2026-06-30 09:30:43 +02:00
Jeppe Bundgaard 5f13242cfa Fix eligibility check by ensuring user is not null in lane access condition 2026-06-29 15:35:26 +02:00
Jeppe Bundgaard b492292642 Add machine wash configuration and update self-serve lane service checks 2026-06-29 15:22:22 +02:00
Jeppe Bundgaard ce8ba88b16 Add machine wash configuration and controls to self-serve module 2026-06-29 15:15:29 +02:00
Jeppe Bundgaard 02b6df5e3b Refactor authentication handling and permission checks in self-serve routes 2026-06-29 14:53:58 +02:00
Jeppe Bundgaard 6cc4f2759d Enhance self-serve functionality by adding lane availability checks and updating response data 2026-06-29 14:25:51 +02:00
Jeppe Bundgaard 3ea92be722 Add endpoint and functionality to test Slack internal department goal progress webhook 2026-06-29 11:32:47 +02:00
Jeppe Bundgaard 42f8ae0c47 Refactor input value handling and improve validation in ObjectsGlobal component 2026-06-29 11:08:50 +02:00
Jeppe Bundgaard 148b575767 Refactor input value handling and improve validation in ObjectsGlobal component 2026-06-29 08:28:00 +02:00
Jeppe B 605efacece Merge pull request #284 from copenhagentruckwash/codex/optimize-scanner-lpr-backend
[codex] optimize scanner LPR backend
2026-06-12 22:08:35 +02:00
Jeppe Bundgaard 1ccd7749d0 optimize scanner lpr backend 2026-06-12 21:42:36 +02:00
Jeppe B b3ba3c8de5 Merge pull request #283 from copenhagentruckwash/fix/pwa-selfserve-stop-latency
[codex] Reduce self-serve latency and add PHP-FPM workers
2026-06-12 13:15:28 +02:00
Jeppe Bundgaard 4a65b669bd Fix CI FPM worker config test 2026-06-12 12:35:06 +02:00
Jeppe Bundgaard aaec443140 Configure multiple PHP-FPM workers 2026-06-12 12:27:27 +02:00
Jeppe Bundgaard 3cdf1571c5 Avoid duplicate self-serve stop relay cleanup 2026-06-12 11:48:51 +02:00
Jeppe Bundgaard 36b934e835 Increase password reset token validity to 72 hours and update related email message 2026-06-11 21:45:22 +02:00
Jeppe B 5027d0c919 Merge pull request #282 from copenhagentruckwash/codex/register-cvr-welcome-email-fix
[codex] Fix register CVR welcome email rendering
2026-06-11 21:22:28 +02:00
Jeppe Bundgaard f0baadd59f Register welcome email legacy test 2026-06-11 21:08:28 +02:00
Jeppe Bundgaard 19cacebaa1 Fix register CVR welcome email rendering 2026-06-11 21:01:34 +02:00
Jeppe Bundgaard 4d9d61455f Refactor company phone number registration error handling and enhance CVR lookup test cases 2026-06-11 20:25:16 +02:00
Jeppe Bundgaard fc87b3a8aa Improve CVR lookup error handling and unify phone number registration error messages 2026-06-11 20:17:48 +02:00
Jeppe Bundgaard 8e0936001d Fix company phone number registration error messages for clarity 2026-06-11 20:03:55 +02:00
Jeppe B af8968a87e Merge pull request #281 from copenhagentruckwash/codex/customer-registration-notifications
Add Slack customer registration webhook test endpoint
2026-06-11 15:18:07 +02:00
Jeppe Bundgaard e6a18ce5d8 Add Slack customer registration webhook test endpoint 2026-06-11 15:06:31 +02:00
Jeppe B b5c24ef80a Merge pull request #280 from copenhagentruckwash/fix/self-serve-path-outcome-case-limit
Fix self-serve path outcome case limit
2026-06-11 14:57:16 +02:00
Jeppe Bundgaard df7153a5ba Fix self-serve path outcome case limit 2026-06-11 14:46:27 +02:00
Jeppe Bundgaard d06c78119b Fix customer registration duplicate recovery 2026-06-11 12:04:56 +02:00
Jeppe B bdb1a0074b Merge pull request #279 from copenhagentruckwash/fix/self-serve-customer-property-gates
Allow customers to open property gates for active washes
2026-06-10 22:00:06 +02:00
Jeppe Bundgaard c0de0e9d6b Allow customers to open property gates for active washes 2026-06-10 21:00:18 +02:00
Jeppe B 574b263a54 Merge pull request #278 from copenhagentruckwash/fix/self-serve-start-wash-type
Honor wash type in self-serve lane start
2026-06-10 20:07:46 +02:00
Jeppe B 36ff5bb438 Merge pull request #277 from copenhagentruckwash/fix-completion-confirmation-route
Add order booking completion confirmation resend route
2026-06-10 20:07:30 +02:00
Jeppe Bundgaard 1beca924fc Fallback composer installs to source in CI 2026-06-10 19:47:17 +02:00
Jeppe B d605eca574 Fallback composer installs to source in CI 2026-06-10 19:22:52 +02:00
Jeppe Bundgaard cea469c95a Honor wash type in self-serve lane start 2026-06-10 19:18:19 +02:00
Jeppe B 7e85c74e60 Retry composer installs in CI 2026-06-10 19:12:12 +02:00
Jeppe B 67d62eff70 Sync fake email deliveries across API tests 2026-06-10 18:53:13 +02:00
Jeppe B 8ebbd52a99 Normalize attachment object type lookups 2026-06-10 18:40:34 +02:00
Jeppe B 6d6cc501db Force completion confirmation resend email 2026-06-10 18:33:53 +02:00
Jeppe B ce999afbb3 Fix MinIO local test storage fallback 2026-06-10 18:22:53 +02:00
Jeppe B cb34b030c8 Add order booking completion confirmation resend route 2026-06-10 17:55:57 +02:00
Jeppe Bundgaard e26034dfae Refactor dynamic image export methods to use binary output and improve caching logic 2026-06-09 14:48:46 +02:00
Jeppe Bundgaard 0aad41fd0f Add program picker relay status handling for wash start and update related tests 2026-06-09 14:09:49 +02:00
Jeppe Bundgaard 5c67fe419f Add timeout settings for Shelly cloud HTTP requests and update related tests 2026-06-09 14:03:12 +02:00
Jeppe Bundgaard aca8be51dc Implement move collected invoice to customer functionality with API endpoint and associated tests 2026-06-09 13:12:55 +02:00
Jeppe Bundgaard fb1f0883e1 Add selfserve dynamic image sizing config 2026-06-09 12:46:14 +02:00
Jeppe Bundgaard 6446eb2e36 Test manual wash program picker selection 2026-06-09 12:05:27 +02:00
Jeppe Bundgaard a1224ec2f4 Fix self-serve program picker wash type sync 2026-06-09 11:59:42 +02:00
Jeppe Bundgaard 07441c4ed1 Harden API auto deploy gate 2026-06-08 18:44:25 +02:00
Jeppe Bundgaard cd100f1180 Invalidate cached session payloads on notification preferences update; enhance flag tab filtering logic 2026-06-08 18:40:15 +02:00
Jeppe Bundgaard 4fc66c72b8 Prefer selected Coolify deployment commit 2026-06-08 18:14:45 +02:00
Jeppe Bundgaard a3ea5fee83 Clean up stale Coolify API routes 2026-06-08 18:02:08 +02:00
Jeppe Bundgaard ef8d97c821 Verify API release commit in gateway gate 2026-06-08 17:48:01 +02:00
Jeppe Bundgaard 327a77edf4 Require API gateway release check 2026-06-08 17:07:49 +02:00
Jeppe Bundgaard d8abc8f87d Refactor session relay synchronization logic for improved clarity 2026-06-08 16:59:53 +02:00
Jeppe Bundgaard 325b35beb7 Add session synchronization tests and ensure atomic session closure 2026-06-08 16:53:24 +02:00
Jeppe Bundgaard 49364864d2 Implement session mutation locking and enhance session management methods 2026-06-08 16:49:41 +02:00
Jeppe Bundgaard a19178a042 Add PHPStan and Rector configuration files for static analysis and code quality 2026-06-08 16:33:37 +02:00
Jeppe Bundgaard 91d3332d4e Add Slack customer registration notification functionality 2026-06-08 12:42:03 +02:00
Jeppe Bundgaard bedbf21c29 Add superuser new customer email notification preferences 2026-06-08 12:20:07 +02:00
Jeppe Bundgaard 75c19bcce4 Fix MyWash active summary refresh import 2026-06-04 08:31:51 +02:00
Jeppe Bundgaard 458fe7399d Persist MyWash sessions on start command 2026-06-04 08:18:31 +02:00
Jeppe Bundgaard 2b6a8eedcc Avoid session creation on MyWash summary refresh 2026-06-04 08:07:40 +02:00
Jeppe Bundgaard c0ed107f75 Resolve MyWash services from published config 2026-06-04 07:50:14 +02:00
Jeppe Bundgaard 30dceff0b5 Avoid self-serve preview sessions on eligibility reads 2026-06-04 06:55:59 +02:00
Jeppe Bundgaard 716929bd7b Inject frontend commit SHA into Coolify runtime environment for manifest builds 2026-06-04 00:38:31 +02:00
Jeppe Bundgaard 3d221f3379 Merge remote-tracking branch 'origin/master' 2026-06-03 21:03:27 +02:00
Jeppe B 6f1c160fbb Sync generated Copilot workflow 2026-06-03 20:57:06 +02:00
Jeppe Bundgaard 9694695f00 Sync generated Copilot workflow 2026-06-03 20:56:06 +02:00
Jeppe Bundgaard 1d43221b4d Sync self-serve machine relay session state 2026-06-03 20:35:50 +02:00
Jeppe Bundgaard 33b7c3e51a Widen self-serve task descriptions 2026-06-03 19:06:09 +02:00
Jeppe Bundgaard 1e64bd63b8 Update PHPUnit test results cache with latest version and defect counts 2026-06-03 18:19:45 +02:00
Jeppe B bcbc2481c3 Source self-serve lane products from published config 2026-06-02 19:05:06 +02:00
Jeppe B 8288a1069c Merge pull request #276
coolify-github-runner-management
2026-06-02 17:27:42 +02:00
Jeppe Bundgaard 1b99523366 Enhance self-serve lane functionality with new relay management and configuration updates 2026-06-02 17:27:22 +02:00
Jeppe Bundgaard 6d739cfebc Add configuration for GitHub self-hosted runners 2026-06-02 11:25:00 +02:00
Jeppe Bundgaard 20071166f8 Switch CI to self-hosted runners
Updated all GitHub Actions workflows to use self-hosted runners instead of `ubuntu-latest`. This change ensures better control over the CI environment and aligns with internal infrastructure requirements.
2026-06-02 10:36:22 +02:00
Jeppe Bundgaard c23168afc5 Merge remote-tracking branch 'origin/master' 2026-06-02 10:29:25 +02:00
Jeppe Bundgaard 72704b7806 Add "Get My Active Self-Serve Wash" endpoint and corresponding tests
- Introduced a new `/modules/self-serve/lane/wash/my-active-wash` endpoint to retrieve the authenticated customer's active self-serve wash.
- Implemented authentication and permission checks for secure access.
- Added detailed response handling for various scenarios, including 401, 403, and 404 statuses.
- Extended API documentation and OpenAPI spec to support the new endpoint.
- Updated unit and API tests to validate endpoint functionality and route wiring.
2026-06-02 10:29:15 +02:00
Jeppe B f7485f0767 Merge pull request #273 from copenhagentruckwash/update-self-serve-lane-command-access-logic
Allow customer self-serve lane commands
2026-06-02 10:22:21 +02:00
copilot-swe-agent[bot] 975909b6a1 Resolve merge conflict with master in SelfserveLaneCommandApiTest.php 2026-06-02 08:15:18 +00:00
Jeppe B 1468e43ce2 Merge pull request #272 from copenhagentruckwash/add-endpoint-to-resend-booking-confirmations
Add booking confirmation resend endpoint
2026-06-02 10:12:29 +02:00
Jeppe B ee2af5091c Retry CI docker compose startup 2026-06-02 10:08:01 +02:00
Jeppe B 0672a68e8b Merge pull request #274 from copenhagentruckwash/update-self-serve-lane-command-access-logic-bft43z
Support customer self-serve lane commands with operational/department checks and tests
2026-06-02 10:07:27 +02:00
copilot-swe-agent[bot] c8804bc8dc Merge master into branch resolving self-serve lane command conflicts 2026-06-02 08:01:46 +00:00
Jeppe B b92d1f0bdf Fix self-serve lane command API tests 2026-06-02 09:52:44 +02:00
copilot-swe-agent[bot] cc10371346 Resolve merge conflict with master in moduleSelfServeRoute.php 2026-06-02 07:40:01 +00:00
Jeppe B ac60596218 Fix booking confirmation resend test fixture 2026-06-02 09:36:36 +02:00
Jeppe B f4b9d71d40 Merge pull request #270 from copenhagentruckwash/add-customer-self-serve-module-authorization-checks
Guard customer self-serve command fallback behind global module flag
2026-06-02 09:25:00 +02:00
Jeppe B 77b1c8ec78 Merge pull request #271 from copenhagentruckwash/inspect-command-authorization-for-self-serve-route
Authorize self-serve lane commands by customer scope and operator permission
2026-06-02 09:24:38 +02:00
Jeppe B 01221d8282 Allow customer self-serve lane commands 2026-06-02 09:24:33 +02:00
Jeppe B 47068e6d7e Add booking confirmation resend endpoint 2026-06-02 09:15:34 +02:00
Jeppe B 46bdeded78 Fix self-serve lane command customer authorization 2026-06-02 09:15:24 +02:00
Jeppe B eefa521fc5 Guard customer self-serve commands behind module flag 2026-06-02 09:14:57 +02:00
Jeppe B ec1988715d Merge pull request #269 from copenhagentruckwash/fix-parse-error-in-index.php
Handle Release Manager gate parse-error deadlock
2026-06-02 02:53:54 +02:00
Jeppe B c3fb2e8651 Handle release gate parse-error deadlock 2026-06-02 02:50:02 +02:00
Jeppe B 0fb279fc5f Merge pull request #268 from copenhagentruckwash/investigate-and-fix-failing-tests
Resolve PHP merge conflicts and restore search/autoload behavior
2026-06-02 02:33:35 +02:00
Jeppe B 3e970d9cb9 Seed subuser session cache in API fixtures 2026-06-02 02:29:52 +02:00
Jeppe B 8c10c07cc9 Resolve Caddy replication bootstrap conflict 2026-06-02 02:25:29 +02:00
Jeppe B 18a8513b40 Use namespaced subuser object in API fixtures 2026-06-02 02:21:53 +02:00
Jeppe B 4c77b78c6c Keep self-serve invoice billing customer authoritative 2026-06-02 02:15:27 +02:00
Jeppe B bb249da477 Align API tests with hardened auth and department access 2026-06-02 02:09:14 +02:00
Jeppe B eb16a4e6ce Fix collected invoice queue count expectations 2026-06-02 02:00:35 +02:00
Jeppe B a2e525fa9e Update unit expectations for hardened flows 2026-06-02 01:53:58 +02:00
Jeppe B 0bf19c9d33 Restrict indexed department filters to scoped entities 2026-06-02 01:34:21 +02:00
Jeppe B 5850bfbce7 Keep autoload cache validation test compatible 2026-06-02 01:16:20 +02:00
Jeppe B fd51a5b119 Fix search table argument ordering 2026-06-02 01:07:45 +02:00
Jeppe B 140365c8bb Resolve PHP merge conflict test failures 2026-06-02 00:58:15 +02:00
Jeppe B c9ceac8533 Merge pull request #267 from copenhagentruckwash/fix-permission-checks-for-subuser-endpoints
Require SUBUSERS_LIST permission for GET /subusers to enforce RBAC
2026-06-02 00:42:48 +02:00
copilot-swe-agent[bot] 4266b933f5 Merge remote-tracking branch 'origin/master' into fix-permission-checks-for-subuser-endpoints
# Conflicts:
#	services/nginx/app/routes/subusersRoute.php
2026-06-01 22:41:35 +00:00
Jeppe B 72ec62d042 Merge pull request #259 from copenhagentruckwash/fix-redis-autoload-cache-vulnerability
Harden Redis-backed autoloader against poisoned path inclusion
2026-06-02 00:37:33 +02:00
copilot-swe-agent[bot] d24b50f751 Plan: Resolve merge conflicts in index.php autoloader 2026-06-01 22:36:09 +00:00
Jeppe B 21f5e6d9cf Enforce permission check on subuser list endpoint 2026-06-02 00:35:48 +02:00
Jeppe B 73b91ccec9 Merge pull request #265 from copenhagentruckwash/fix-unauthenticated-bird-voice-webhook
Reinstate authorization check for Bird inbound voice webhook
2026-06-02 00:33:43 +02:00
Jeppe B 0c809a19da Merge pull request #257 from copenhagentruckwash/propose-fix-for-redis-image-cache-vulnerability
Limit Redis dynamic image caching to default variant only
2026-06-02 00:33:27 +02:00
Jeppe B 3a6685c345 Merge pull request #255 from copenhagentruckwash/fix-system-search-authorization-bypass
Enforce department scoping in system search for generic entities
2026-06-02 00:33:02 +02:00
Jeppe B a60983f328 Merge pull request #266 from copenhagentruckwash/propose-fix-for-n8n-ssrf-vulnerability
Harden n8n webhook trigger URL validation against SSRF
2026-06-02 00:32:47 +02:00
copilot-swe-agent[bot] 51c619b0c6 Resolve merge conflicts in departmentLanesRoute.php 2026-06-01 22:29:49 +00:00
copilot-swe-agent[bot] fe9daf1bf2 Merge remote-tracking branch 'origin/master' into fix-unauthenticated-bird-voice-webhook
# Conflicts:
#	services/nginx/app/routes/birdVoiceWebhooksRoute.php
2026-06-01 22:27:40 +00:00
copilot-swe-agent[bot] 9c2d7140b4 Merge master into branch to resolve conflicts 2026-06-01 22:27:04 +00:00
copilot-swe-agent[bot] 1505464095 Plan: Resolve merge conflicts with master 2026-06-01 22:25:49 +00:00
Jeppe B cc00fb2aed Reinstate auth on Bird inbound voice webhook 2026-06-02 00:23:58 +02:00
Jeppe B 7f38cf2f7e Merge pull request #264 from copenhagentruckwash/propose-fix-for-ssrf-in-workfeed-api
Restrict Workfeed API base URL to trusted hosts (prevent SSRF)
2026-06-02 00:22:35 +02:00
Jeppe B d281dddbc1 Restrict Workfeed API base URL 2026-06-02 00:22:18 +02:00
Jeppe B 267ec1bed1 Merge pull request #263 from copenhagentruckwash/fix-machine-relay-set-endpoint-vulnerability
Guard machine relay set status
2026-06-02 00:21:49 +02:00
Jeppe B a96f40cf13 Guard machine relay set status 2026-06-02 00:21:32 +02:00
Jeppe B d6190626ce Merge pull request #262 from copenhagentruckwash/fix-cross-tenant-job-data-exposure
Scope economic transfer queue jobs by creator
2026-06-02 00:20:48 +02:00
Jeppe B ce8e6d0dab Scope economic transfer queue jobs by creator 2026-06-02 00:20:30 +02:00
Jeppe B c13c2e2cab Merge pull request #261 from copenhagentruckwash/fix-customer-data-leak-in-wash-endpoint
Restrict in-progress wash details by lane department
2026-06-02 00:20:07 +02:00
Jeppe B 7380bc729b Restrict in-progress wash details by lane department 2026-06-02 00:19:55 +02:00
Jeppe B 434a5049e2 Merge pull request #260 from copenhagentruckwash/fix-unpinned-github-actions-vulnerability
Harden Qodana workflow permissions and pin checkout action
2026-06-02 00:17:44 +02:00
copilot-swe-agent[bot] 6489706231 Merge master and resolve conflicts
- Retained security improvements from master (token detection, cache prep, safe directory)
- Applied security hardening by pinning actions/checkout@v4 to commit SHA 11bd71901bbe5b1630ceea73d27597364c9af683
- Added persist-credentials: false to checkout step to prevent credential exposure
2026-06-01 22:14:21 +00:00
Jeppe B eb66b343ea Harden Qodana workflow permissions and checkout pin 2026-06-02 00:04:10 +02:00
Jeppe B e363f27da9 Harden autoload Redis cache path validation 2026-06-02 00:02:40 +02:00
Jeppe B fbad5f767f Merge pull request #258 from copenhagentruckwash/fix-hardcoded-auth-tokens-in-configuration
Sanitize leaked auth tokens in HTTP test env
2026-06-02 00:02:06 +02:00
Jeppe B 94d9b347bf Sanitize leaked auth tokens in HTTP test env 2026-06-02 00:01:57 +02:00
Jeppe B 76744fd6c3 Limit dynamic image Redis caching to default variant 2026-06-02 00:00:04 +02:00
Jeppe B f5c1a34c29 Merge pull request #256 from copenhagentruckwash/fix-idor-vulnerability-in-economic-v2-endpoints
Prevent IDOR on Economic V2 collected-invoice endpoints
2026-06-01 23:58:41 +02:00
Jeppe B 22ad96bc8e Fix economic v2 invoice endpoint authorization scope 2026-06-01 23:58:30 +02:00
Jeppe B 8a749cffa3 Fix system search department scoping for generic entities 2026-06-01 23:58:03 +02:00
Jeppe B cf5cf8d5eb Merge pull request #254 from copenhagentruckwash/fix-user-search-exposure-vulnerability
Restrict `users` system-search access to prevent PII leakage
2026-06-01 23:57:33 +02:00
Jeppe B eb14b7039b Restrict users system search permissions 2026-06-01 23:57:23 +02:00
Jeppe B f09b1263c1 Merge pull request #253 from copenhagentruckwash/fix-stripe-payment-intent-reuse-issue
Validate Stripe payment intent amount before reuse
2026-06-01 23:54:32 +02:00
Jeppe B 9ec8499d55 Validate Stripe payment intent amount before reuse 2026-06-01 23:54:04 +02:00
Jeppe B 8d40cd6f9a Merge pull request #252 from copenhagentruckwash/fix-complaint-endpoints-department-access-check
Require department access for department daily report complaint routes
2026-06-01 23:53:45 +02:00
Jeppe B ccffad3c7c Fix complaint department authorization 2026-06-01 23:53:36 +02:00
Jeppe B 4697c6b272 Merge pull request #251 from copenhagentruckwash/fix-subuser-management-permission-checks
Enforce own-scope subuser permissions for classic users in managed customer scope
2026-06-01 23:53:06 +02:00
Jeppe B 45e17e196c Fix subuser management permission scope 2026-06-01 23:52:57 +02:00
Jeppe B dcc81cbdc7 Merge pull request #250 from copenhagentruckwash/propose-fix-for-privilege-boundary-regression
Restrict studio simulation to config-version view and prevent auto-creating drafts
2026-06-01 23:52:28 +02:00
Jeppe B c5cb0a3bfe Fix studio simulation draft access 2026-06-01 23:52:18 +02:00
Jeppe B 465f3ed027 Merge pull request #249 from copenhagentruckwash/fix-edge-agent-vulnerability-for-unsigned-artifacts
Require checksums for edge agent updates
2026-06-01 23:50:05 +02:00
Jeppe B 80ff01f04e Require checksums for edge agent updates 2026-06-01 23:49:56 +02:00
Jeppe B f6e4d851d3 Merge pull request #248 from copenhagentruckwash/fix-authenticated-ssrf-in-broker-diagnostics
Prevent SSRF in broker diagnostics by ignoring caller URLs and redacting probe output
2026-06-01 23:49:24 +02:00
Jeppe B cd4e3faea3 Fix broker diagnostics SSRF 2026-06-01 23:49:10 +02:00
Jeppe B 4cb9e68b33 Merge pull request #247 from copenhagentruckwash/fix-information-disclosure-in-websocket-upgrades
Sanitize websocket upgrade error responses
2026-06-01 23:47:09 +02:00
Jeppe B 0e7e79d205 Sanitize websocket upgrade errors 2026-06-01 23:46:59 +02:00
Jeppe B a9ca7b41a7 Merge pull request #246 from copenhagentruckwash/fix-unbounded-relay-timer-vulnerability
Cap self-serve gate relay timers
2026-06-01 23:45:39 +02:00
Jeppe B 3b3ed31bb7 Cap self-serve gate relay timers 2026-06-01 23:45:20 +02:00
Jeppe B cf9d5875ef Merge pull request #242 from copenhagentruckwash/fix-vulnerability-with-self-hosted-runners
Run PR code quality workflow on GitHub-hosted runner
2026-06-01 23:44:29 +02:00
Jeppe B 4730eebdb4 Merge pull request #245 from copenhagentruckwash/fix-internal-ip-address-leakage
Stop exposing relay local IPs by default
2026-06-01 23:44:05 +02:00
Jeppe B b25ce9cb11 Stop exposing relay local IPs by default 2026-06-01 23:43:53 +02:00
Jeppe B fdb98f1399 Merge pull request #244 from copenhagentruckwash/fix-unauthenticated-relay-control-vulnerability
Require authorization for LAN worker relay endpoints
2026-06-01 23:43:34 +02:00
Jeppe B 1dc758a3a3 Require authorization for LAN worker relay endpoints 2026-06-01 23:43:23 +02:00
Jeppe B 032ce93d5e Merge pull request #243 from copenhagentruckwash/fix-hardcoded-service-credentials-in-docker-config
Secure edge gateway service credentials
2026-06-01 23:42:47 +02:00
Jeppe B f8ced3b8f2 Secure edge gateway service credentials 2026-06-01 23:42:34 +02:00
copilot-swe-agent[bot] a81e239de8 Merge master into branch and resolve code_quality.yml comment conflict 2026-06-01 21:42:21 +00:00
Jeppe B 9ea5a62577 Merge pull request #238 from copenhagentruckwash/fix-cache-only-lookup-for-invoice-flags
Normalize invoice-period cache keys and restore DB fallbacks for missing Redis entries
2026-06-01 23:41:26 +02:00
Jeppe B af89a246db Run PR code quality workflow on GitHub-hosted runner 2026-06-01 23:38:32 +02:00
Jeppe B 20c1973565 Merge pull request #241 from copenhagentruckwash/fix-telemetry-path-error-message-leak
Sanitize telemetry ingestion errors
2026-06-01 23:37:59 +02:00
copilot-swe-agent[bot] 3555904423 Merge origin/master and resolve invoice_period_flag_service conflict 2026-06-01 21:37:53 +00:00
Jeppe B a2dda5ea5b Sanitize telemetry ingestion errors 2026-06-01 23:37:48 +02:00
Jeppe B ce29cf9ccb Merge pull request #240 from copenhagentruckwash/propose-fix-for-ci-vulnerability
Secure Qodana pull request workflow
2026-06-01 23:36:53 +02:00
Jeppe B e7481297c8 Secure Qodana PR workflow runner 2026-06-01 23:36:44 +02:00
Jeppe B e3b38519fb Merge pull request #239 from copenhagentruckwash/propose-fix-for-qodana-vulnerability
Skip Qodana when cloud token is missing
2026-06-01 23:31:34 +02:00
Jeppe B d244c000c3 Skip Qodana when cloud token is missing 2026-06-01 23:31:24 +02:00
Jeppe B dcd57c7092 Merge pull request #221 from copenhagentruckwash/fix-system-search-associations-vulnerability
Prevent association expansion from bypassing own-only access
2026-06-01 23:31:00 +02:00
Jeppe B 0a7e58fc01 Merge pull request #222 from copenhagentruckwash/fix-hardcoded-bearer-tokens-in-tests
Remove hardcoded API credentials and resolve merge conflict in test HTTP file
2026-06-01 23:29:28 +02:00
Jeppe B bd7deaeded Fix invoice period flag cache fallbacks 2026-06-01 23:29:05 +02:00
Jeppe B 07a3ef6418 Merge pull request #237 from copenhagentruckwash/fix-concurrent-access-vulnerability-in-start-command
Add per-lane START lock to prevent TOCTOU relay replay on wash start
2026-06-01 23:28:45 +02:00
Jeppe B bffed6f5f3 Fix self-serve start relay race 2026-06-01 23:28:36 +02:00
Jeppe B bfec31f94b Merge pull request #236 from copenhagentruckwash/fix-task-attachment-link-vulnerability
Enforce lane department authorization for self-serve eligibility
2026-06-01 23:28:05 +02:00
Jeppe B 2123835aae Fix self-serve eligibility lane authorization 2026-06-01 23:27:56 +02:00
Jeppe B 11f06e8f53 Merge pull request #235 from copenhagentruckwash/investigate-self-serve-path-projection-dos-vulnerability
Clamp self-serve path projection limits
2026-06-01 23:27:33 +02:00
Jeppe B 6712368323 Clamp self-serve path projection limits 2026-06-01 23:27:22 +02:00
Jeppe B 99fe659dbc Merge pull request #234 from copenhagentruckwash/propose-fix-for-archived-department-vulnerability
Fix department archived filter smuggling
2026-06-01 23:27:06 +02:00
Jeppe B 357cfda46e Fix department archived filter smuggling 2026-06-01 23:26:57 +02:00
Jeppe B 9c85135a07 Merge pull request #233 from copenhagentruckwash/fix-cross-tenant-vehicle-reference-leak
Restrict vehicle reference suggestions by department context
2026-06-01 23:26:36 +02:00
Jeppe B a8a47104dd Restrict vehicle reference suggestions by department context 2026-06-01 23:26:27 +02:00
Jeppe B 2c0907c486 Merge pull request #232 from copenhagentruckwash/fix-vulnerability-in-automatic-invoice-flags
Fix automatic invoice period flag suppression
2026-06-01 23:26:02 +02:00
copilot-swe-agent[bot] b1647b4ad1 Merge origin/master and resolve orderBookingsPost conflict 2026-06-01 21:25:58 +00:00
Jeppe B 0ae28af309 Fix invoice period automatic flag cache misses 2026-06-01 23:25:54 +02:00
copilot-swe-agent[bot] 64d7e6f061 Merge master into fix-system-search-associations-vulnerability 2026-06-01 21:25:51 +00:00
Jeppe B 4f9a10402b Merge pull request #231 from copenhagentruckwash/fix-exposure-of-private-git-commit-metadata
Redact GitHub commit metadata from public release runtime
2026-06-01 23:25:38 +02:00
Jeppe B 5e8ec85943 Redact release GitHub metadata from public runtime 2026-06-01 23:25:28 +02:00
Jeppe B 20d6056e40 Merge pull request #230 from copenhagentruckwash/fix-permission-bypass-for-invoice-flags
Guard invoice period flags by list permission
2026-06-01 23:25:02 +02:00
Jeppe B 5be6bc0198 Guard invoice period flags by list permission 2026-06-01 23:24:50 +02:00
Jeppe B 373aa7effb Merge pull request #227 from copenhagentruckwash/fix-minio-credentials-exposure-vulnerability
Deny web access to replication bootstrap snapshots
2026-06-01 23:24:26 +02:00
Jeppe B 5282ee10ba Merge pull request #229 from copenhagentruckwash/propose-fix-for-booking-po-vulnerability
Validate booking ownership before defaulting order PO (prevent cross-tenant leak)
2026-06-01 23:24:00 +02:00
copilot-swe-agent[bot] 06cba73a30 Initialize merge conflict resolution plan 2026-06-01 21:23:50 +00:00
Jeppe B eab8394579 Fix booking PO default tenant validation 2026-06-01 23:23:49 +02:00
Jeppe B b88c2742e8 Merge pull request #228 from copenhagentruckwash/fix-partial-release-tests-bypassing-promotion-gate
Require app-scoped release gates for bundle promotion
2026-06-01 23:23:34 +02:00
Jeppe B 4ea5eeb942 Require app-scoped release gates for bundle promotion 2026-06-01 23:23:23 +02:00
Jeppe B e41b226529 Deny web access to replication bootstrap snapshots 2026-06-01 23:19:04 +02:00
Jeppe B 7cb248a112 Merge pull request #226 from copenhagentruckwash/fix-ssrf-vulnerability-in-release-gate
Harden release gate diagnostics fetches
2026-06-01 23:17:01 +02:00
Jeppe B dfa0441266 Harden release gate diagnostics fetches 2026-06-01 23:16:51 +02:00
Jeppe B d9dbd7dede Merge pull request #225 from copenhagentruckwash/propose-fix-for-coolify-deployment-vulnerability
Prevent Coolify image from embedding replication snapshots
2026-06-01 23:16:33 +02:00
Jeppe B 3b8463e37f Prevent Coolify image from embedding replication snapshots 2026-06-01 23:16:22 +02:00
Jeppe B 0e8b527ee4 Merge pull request #224 from copenhagentruckwash/fix-qodana-scan-fail-open-issue
Run Qodana locally when cloud token is missing
2026-06-01 23:15:47 +02:00
Jeppe B 86fb8bb700 Run Qodana without upload when token is missing 2026-06-01 23:15:36 +02:00
Jeppe B 6fbf7f271d Merge pull request #223 from copenhagentruckwash/propose-fix-for-exposed-bootstrap-secret
Protect replication bootstrap file from static serving
2026-06-01 23:14:52 +02:00
Jeppe B fe5ebdc203 Protect replication bootstrap file from static serving 2026-06-01 23:14:43 +02:00
Jeppe B c6dbc0728f Remove hardcoded credentials from orderBookingsPost HTTP examples 2026-06-01 23:14:12 +02:00
Jeppe B a0b1dcb3e3 Fix system search association expansion for own-only types 2026-06-01 23:13:50 +02:00
Jeppe B 38c4c32f07 Merge pull request #220 from copenhagentruckwash/fix-empty-edge-broker-secret-vulnerability
Fail closed when edge broker secret is missing
2026-06-01 23:13:13 +02:00
Jeppe B b6beb9622b Fail closed when edge broker secret is missing 2026-06-01 23:13:04 +02:00
Jeppe B db80dad15f Merge pull request #219 from copenhagentruckwash/propose-fix-for-unauthenticated-pdf-access
Fix unauthenticated PDF disclosure in file_server fallback
2026-06-01 23:12:00 +02:00
Jeppe B cf370a8035 Fix unauthenticated pdf_store access in file server 2026-06-01 23:11:48 +02:00
Jeppe B 0778776f00 Merge pull request #218 from copenhagentruckwash/fix-machine-relay-helper-logic
Fix hard MACHINE relay targeting
2026-06-01 23:11:13 +02:00
Jeppe B ee55c23cde Fix hard machine relay targeting 2026-06-01 23:11:01 +02:00
Jeppe B 1f50c83f93 Merge pull request #217 from copenhagentruckwash/fix-unauthenticated-/files/-attachment-access
Require authentication for direct /files/ access
2026-06-01 23:09:47 +02:00
Jeppe B 85f7bd1fc9 Require auth for direct /files/ downloads 2026-06-01 23:09:37 +02:00
Jeppe B 8f53e80ede Merge pull request #216 from copenhagentruckwash/fix-vulnerability-in-wash-certificate-access
Disable global .pdf shortcut to prevent unauthenticated certificate downloads
2026-06-01 23:09:03 +02:00
Jeppe B 2a1a730a8c Fix unauthenticated direct PDF certificate serving 2026-06-01 23:08:53 +02:00
Jeppe B 7bb67b0470 Merge pull request #213 from copenhagentruckwash/fix-sql-injection-in-vehicle-plate-lookup
Fix SQL injection in vehicle plate order history lookup
2026-06-01 23:08:35 +02:00
copilot-swe-agent[bot] 1065973b33 Merge master into fix-sql-injection-in-vehicle-plate-lookup 2026-06-01 21:07:49 +00:00
Jeppe B 1d05550cd3 Merge pull request #215 from copenhagentruckwash/fix-start-command-relay-activation-vulnerability
Fix self-serve START relay deferral bypass
2026-06-01 23:05:10 +02:00
Jeppe B 2cc12c23cd Fix self-serve start relay deferral 2026-06-01 23:04:59 +02:00
Jeppe B b09ada0bc4 Merge pull request #214 from copenhagentruckwash/fix-hardcoded-auth_key-in-bookings-sync
Remove hardcoded auth_key bypass in admin bookings sync endpoint
2026-06-01 23:03:56 +02:00
Jeppe B 43dfac836a Fix booking sync auth bypass 2026-06-01 23:03:47 +02:00
Jeppe B d1871f1420 Fix SQL injection in vehicle plate order history lookup 2026-06-01 23:03:18 +02:00
Jeppe B 08a1538ed6 Merge pull request #212 from copenhagentruckwash/propose-fix-for-sql-injection-vulnerability
Cast pickup_bool to int to prevent SQL injection in bookings sync
2026-06-01 23:02:38 +02:00
Jeppe B f2fc4f6f18 Fix SQL injection risk in booking sync pickup_bool 2026-06-01 23:02:28 +02:00
Jeppe B 503fd50c61 Merge pull request #211 from copenhagentruckwash/fix-vulnerability-in-wash-certificate-pdf-handling
Restore deletion of local wash certificate PDFs after upload
2026-06-01 23:02:12 +02:00
Jeppe B 1d6df82c1c Delete local wash certificate PDFs after upload 2026-06-01 23:02:01 +02:00
Jeppe B 3fda0f9912 Merge pull request #210 from copenhagentruckwash/fix-auth-bypass-in-booking-sync-endpoint
Remove hardcoded auth_key bypass from /admin/bookings/sync
2026-06-01 23:01:37 +02:00
Jeppe B decc571307 Fix booking sync auth bypass 2026-06-01 23:01:28 +02:00
Jeppe B ef237b5e87 Merge pull request #206 from copenhagentruckwash/fix-sql-injection-in-vehicle-plate-history
Fix SQL injection in vehicle plate order history lookup
2026-06-01 22:59:14 +02:00
Jeppe B 828c177a57 Merge pull request #207 from copenhagentruckwash/fix-order-item-update-idor-vulnerability
Enforce tenant ownership check for PUT /order/items to prevent IDOR
2026-06-01 22:59:03 +02:00
copilot-swe-agent[bot] c79219eb00 Merge remote-tracking branch 'origin/master' into fix-order-item-update-idor-vulnerability
# Conflicts:
#	services/nginx/app/routes/orderItemsRoute.php
2026-06-01 20:58:04 +00:00
copilot-swe-agent[bot] 6995c3d1bc Merge origin/master and resolve orders_o conflict 2026-06-01 20:57:44 +00:00
Jeppe B 7436584598 Merge pull request #209 from copenhagentruckwash/fix-unauthenticated-certificate-download-vulnerability
Require authentication token for wash certificate download endpoint
2026-06-01 22:57:33 +02:00
Jeppe B 06421beb6b Require token for wash certificate downloads 2026-06-01 22:57:23 +02:00
Jeppe B 7de4b96074 Merge pull request #208 from copenhagentruckwash/fix-arbitrary-group_id-role-assignment
Harden role authorization on user creation
2026-06-01 22:56:08 +02:00
Jeppe B ea69c64fad Harden user creation role authorization 2026-06-01 22:55:57 +02:00
Jeppe B 652b89d23d Fix IDOR in order item update route 2026-06-01 22:55:35 +02:00
Jeppe B bc4b7bde15 Fix SQL injection in vehicle plate order history lookup 2026-06-01 22:55:06 +02:00
Jeppe B a5b674286a Merge pull request #205 from copenhagentruckwash/fix-order-update-vulnerability-for-invoice-collection
Validate invoice collection ownership when updating orders
2026-06-01 22:54:31 +02:00
copilot-swe-agent[bot] 18bf7aa013 Resolve merge conflict: combine invoice collection ownership validation with auto-reassign guard 2026-06-01 20:53:42 +00:00
Jeppe B bf8262b64f Validate invoice collection ownership when updating orders 2026-06-01 22:51:09 +02:00
Jeppe B fdb073f17f Merge pull request #204 from copenhagentruckwash/fix-unauthenticated-sync-usage-endpoint
Enforce permission on XLVask sync-usage route
2026-06-01 22:50:42 +02:00
Jeppe B 20fcd4ac16 Protect XLVask sync-usage route with permission check 2026-06-01 22:50:33 +02:00
Jeppe B 0f7d76d96d Merge pull request #203 from copenhagentruckwash/fix-unauthenticated-limble-endpoints
Enforce Limble route permissions and secure Limble HTTP requests
2026-06-01 22:50:10 +02:00
Jeppe B 324f2c856f Fix Limble auth and secure request handling 2026-06-01 22:50:00 +02:00
Jeppe B 84f203939c Merge pull request #202 from copenhagentruckwash/fix-unauthenticated-limble-webhook-vulnerability
Prevent credential leak in Limble request error path
2026-06-01 22:49:38 +02:00
Jeppe B 368501a8ce Fix Limble request error path credential leak 2026-06-01 22:49:29 +02:00
Jeppe B d9a36e4050 Merge pull request #201 from copenhagentruckwash/fix-idor-vulnerability-in-attachment-endpoints
Ensure attachment belongs to task before download/delete (fix IDOR)
2026-06-01 22:48:14 +02:00
Jeppe B a5019efbda Fix task attachment IDOR in self-serve endpoints 2026-06-01 22:48:04 +02:00
Jeppe B b16a07fdbb Merge pull request #200 from copenhagentruckwash/fix-subuser-permission-vulnerability
Harden subuser permission customer context resolution
2026-06-01 22:46:50 +02:00
Jeppe B ca02fd3436 Harden subuser permission customer context resolution 2026-06-01 22:46:40 +02:00
Jeppe B 65283b8ad7 Merge pull request #196 from copenhagentruckwash/fix-sql-injection-in-recommended-order-lookup
Escape plate input to prevent SQL injection in recommended-order lookup
2026-06-01 22:45:46 +02:00
Jeppe B ad53041bfd Merge pull request #197 from copenhagentruckwash/fix-department-lanes-access-vulnerability
Enforce department scoping in department lanes routes
2026-06-01 22:45:34 +02:00
Jeppe B b11b38a95b Merge pull request #198 from copenhagentruckwash/fix-lane-ownership-validation-for-commands
Enforce department scoping for self-serve lane command route
2026-06-01 22:45:23 +02:00
Jeppe B ba9c4d3b9f Merge pull request #199 from copenhagentruckwash/fix-missing-department-access-checks
Require department-level access for /departments/self-serve/enabled endpoints
2026-06-01 22:45:11 +02:00
copilot-swe-agent[bot] ded497b3d8 Merge remote-tracking branch 'origin/master' into fix-missing-department-access-checks
# Conflicts:
#	services/nginx/app/routes/departmentsRoute.php
2026-06-01 20:43:01 +00:00
copilot-swe-agent[bot] 2fd3ce4877 Merge remote-tracking branch 'origin/master' into fix-department-lanes-access-vulnerability
# Conflicts:
#	services/nginx/app/routes/departmentLanesRoute.php
2026-06-01 20:42:43 +00:00
copilot-swe-agent[bot] 64fc70a0a8 Merge remote-tracking branch 'origin/master' into fix-lane-ownership-validation-for-commands
# Conflicts:
#	services/nginx/app/routes/moduleSelfServeRoute.php
2026-06-01 20:41:57 +00:00
Jeppe B 42acf26ee1 Merge pull request #193 from copenhagentruckwash/fix-subuser-tokens-allowing-user-impersonation
Prevent subuser session token escalation into user auth
2026-06-01 22:41:35 +02:00
Jeppe B fe6eae862f Merge pull request #194 from copenhagentruckwash/fix-missing-department-authorization-for-payment-intents
Require department access on Stripe payment-intent routes
2026-06-01 22:41:24 +02:00
copilot-swe-agent[bot] eedde6c6d7 Merge origin/master and resolve orders_o conflict 2026-06-01 20:41:18 +00:00
copilot-swe-agent[bot] 2782afde2e Merge master into branch and re-apply department access checks on Stripe payment-intent routes 2026-06-01 20:40:30 +00:00
Jeppe B 484529660b Enforce department access on self-serve status routes 2026-06-01 22:39:52 +02:00
copilot-swe-agent[bot] fbe700a4db Merge remote-tracking branch 'origin/master' into fix-subuser-tokens-allowing-user-impersonation
# Conflicts:
#	services/nginx/app/classes/authentication.php
2026-06-01 20:39:16 +00:00
Jeppe B 6225c4b072 Enforce department access for self-serve lane commands 2026-06-01 22:39:14 +02:00
Jeppe B 300a37fce3 Enforce department access in department lanes routes 2026-06-01 22:38:53 +02:00
Jeppe B c43618351e Escape plate in recommended order SQL lookup 2026-06-01 22:37:55 +02:00
Jeppe B b3225c8d8b Merge pull request #195 from copenhagentruckwash/fix-sql-injection-in-filter-handling
Fix SQL injection in array-based pagination filters
2026-06-01 22:37:38 +02:00
Jeppe B 0f96247bf3 Fix SQL injection in array pagination filters 2026-06-01 22:37:28 +02:00
Jeppe B 4703e07951 Enforce department access on Stripe payment intent order routes 2026-06-01 22:36:49 +02:00
Jeppe B 7ddda9ab03 Merge pull request #190 from copenhagentruckwash/fix-2fa-token-validation-bypass
Enforce auth token types to prevent 2FA bypass
2026-06-01 22:36:18 +02:00
copilot-swe-agent[bot] 4e9575cd87 Merge master and resolve conflict: use rawToken in get_user() exception-handled lookup 2026-06-01 20:35:51 +00:00
Jeppe B ef82a95feb Merge pull request #186 from copenhagentruckwash/propose-fix-for-edge-broker-vulnerability
Harden edge broker defaults and restrict compose exposure
2026-06-01 22:35:45 +02:00
Jeppe B fd4ec3dda2 Fix subuser token confusion in user auth flow 2026-06-01 22:35:24 +02:00
Jeppe B 1616bd431a Merge pull request #192 from copenhagentruckwash/fix-subuser-permission-evaluation-vulnerability
Use resolved customer context in subuser permission checks
2026-06-01 22:34:56 +02:00
copilot-swe-agent[bot] 334a7a4401 Merge origin/master into propose-fix-for-edge-broker-vulnerability, resolving conflicts 2026-06-01 20:34:47 +00:00
Jeppe B 22dd9f9c07 Fix subuser permission checks to use resolved customer context 2026-06-01 22:34:45 +02:00
Jeppe B 5684da1bc7 Merge pull request #191 from copenhagentruckwash/fix-sql-injection-in-gate/relay-creation
Escape JSON-encoded values in add_object to prevent SQL injection
2026-06-01 22:34:00 +02:00
Jeppe B 69cd039322 Escape JSON values in add_object inserts 2026-06-01 22:33:49 +02:00
Jeppe B 0dc7f813a8 Merge pull request #188 from copenhagentruckwash/propose-fix-for-relay-control-bypass-vulnerability
Fix self-serve relay sync to enforce lane safety guards
2026-06-01 22:33:11 +02:00
copilot-swe-agent[bot] a828e9bc25 Merge origin/master into propose-fix-for-edge-broker-vulnerability, resolving all conflicts 2026-06-01 20:27:11 +00:00
copilot-swe-agent[bot] 8d2e71aaf3 Merge origin/master and resolve self-serve relay sync conflicts 2026-06-01 20:23:09 +00:00
Jeppe B 721e2670dd Reject 2FA verification tokens for API authentication 2026-06-01 22:22:42 +02:00
Jeppe B b03500d2d1 Merge pull request #189 from copenhagentruckwash/fix-subuser-token-authorization-vulnerability
Validate subuser grants before resolving subuser customer context
2026-06-01 22:21:58 +02:00
Jeppe B ed9ebc2ac8 Validate subuser grants before resolving customer user 2026-06-01 22:21:44 +02:00
Jeppe B 64beb38bae Fix self-serve relay sync to enforce lane safety guards 2026-06-01 22:19:34 +02:00
Jeppe B e13bbae01f Merge pull request #184 from copenhagentruckwash/fix-edge-broker-default-shared-secret-issue
Harden edge broker shared secret defaults
2026-06-01 22:17:57 +02:00
Jeppe B 5ba0f5f9ba Merge pull request #183 from copenhagentruckwash/fix-credential-exposure-in-.env.old
Remove leaked `.env.old` with credentials and add to `.gitignore`
2026-06-01 22:17:30 +02:00
Jeppe B bb5f1db1b3 Merge branch 'master' into fix-credential-exposure-in-.env.old 2026-06-01 22:17:21 +02:00
copilot-swe-agent[bot] cb63d10415 Merge origin/master into fix-edge-broker-default-shared-secret-issue 2026-06-01 20:12:06 +00:00
Jeppe B 4183c3928c Merge pull request #187 from copenhagentruckwash/fix-hard-coded-tokens-in-test-file
Sanitize leaked credentials in test/orderBookingsPost.http
2026-06-01 22:11:38 +02:00
Jeppe B 28bae85b2a Sanitize leaked credentials in order booking HTTP template 2026-06-01 22:11:23 +02:00
Jeppe B f2db92de09 Harden edge broker defaults and compose exposure 2026-06-01 22:10:14 +02:00
Jeppe B a41334f513 Merge pull request #185 from copenhagentruckwash/fix-mysql-debug-exposure-vulnerability
Harden mysql-debug compose service configuration
2026-06-01 22:09:46 +02:00
Jeppe B 175fb3a35f Harden mysql-debug compose service configuration 2026-06-01 22:09:35 +02:00
copilot-swe-agent[bot] 8e6b29810a Clean up resolved gitignore merge 2026-06-01 20:08:24 +00:00
Jeppe B 21e9b2c80f Harden edge broker shared secret defaults 2026-06-01 22:08:20 +02:00
copilot-swe-agent[bot] 989d04167a Resolve .gitignore merge conflict with master 2026-06-01 20:07:48 +00:00
Jeppe B 286127c390 Merge pull request #182 from copenhagentruckwash/fix-edge-broker-default-shared-secret-issue
Remove insecure default edge broker shared secret and stop exposing port 4300
2026-06-01 22:07:10 +02:00
copilot-swe-agent[bot] 2ba87a4850 Start merge conflict resolution 2026-06-01 20:06:07 +00:00
Jeppe B 6658af814b Remove committed env backup with secrets 2026-06-01 22:04:11 +02:00
Jeppe B 2abd6d04e9 Merge pull request #180 from copenhagentruckwash/fix-edge-broker-vulnerability-in-repository
Harden edge broker compose defaults
2026-06-01 22:02:41 +02:00
copilot-swe-agent[bot] 3107779b74 Resolve merge conflicts with origin/master 2026-06-01 20:02:21 +00:00
Jeppe B 61a09dce87 Remove insecure default edge broker secret fallback 2026-06-01 22:01:39 +02:00
Jeppe B a02ed69108 Merge pull request #181 from copenhagentruckwash/fix-remote-root-shell-execution-vulnerability
Gate edge-agent shell actions behind local opt-in
2026-06-01 22:01:01 +02:00
Jeppe B 9b69aadca4 Gate edge-agent shell actions behind local opt-in 2026-06-01 22:00:49 +02:00
Jeppe B 6204fb50f9 Harden edge broker compose defaults 2026-06-01 21:59:27 +02:00
Jeppe B 0a6a8aeab2 Merge pull request #179 from copenhagentruckwash/fix-vulnerability-in-ci-workflow
Harden tests workflow: run PR jobs on GitHub-hosted runners
2026-06-01 21:57:15 +02:00
copilot-swe-agent[bot] 7c21b6463d Merge origin/master and resolve workflow conflicts 2026-06-01 19:55:27 +00:00
Jeppe B d97cfda0ea Harden CI by avoiding self-hosted runners on PR workflow 2026-06-01 21:48:39 +02:00
Jeppe B aad5d77f41 Merge pull request #178 from copenhagentruckwash/propose-fix-for-exposure-of-sensitive-logs
Remove committed Caddy access log containing leaked secrets
2026-06-01 21:47:33 +02:00
Jeppe B 3b132cad95 Merge pull request #176 from copenhagentruckwash/fix-property-gate-command-authorization-bypass
Restore explicit permissions for property gate commands to fix authorization bypass
2026-06-01 21:03:30 +02:00
copilot-swe-agent[bot] ab957092bd Merge origin/master into propose-fix-for-exposure-of-sensitive-logs 2026-06-01 19:03:25 +00:00
copilot-swe-agent[bot] b8f65f242f Merge origin/master and resolve property gate conflict 2026-06-01 19:02:11 +00:00
Jeppe B 688cb0a664 Merge pull request #173 from copenhagentruckwash/fix-cross-tenant-certificate-attachment-vulnerability
Validate booking order context before certificates
2026-06-01 21:00:33 +02:00
Jeppe B 6eb4171fea Merge pull request #172 from copenhagentruckwash/propose-fix-for-automation-permission-bug
Prevent XL Vask list automation execution
2026-06-01 21:00:21 +02:00
Jeppe B ddba27a1be Remove committed Caddy access log with leaked secrets 2026-06-01 20:59:59 +02:00
copilot-swe-agent[bot] 933b18b988 Merge origin/master and resolve booking conflict files 2026-06-01 18:59:05 +00:00
Jeppe B 18c6852865 Merge pull request #177 from copenhagentruckwash/fix-broker-secret-vulnerability-in-api
Harden edge broker shared-secret handling
2026-06-01 20:58:58 +02:00
Jeppe B 77403965f8 Harden edge broker shared-secret handling 2026-06-01 20:58:45 +02:00
copilot-swe-agent[bot] a3e2765ad4 Merge origin/master and resolve XLVask route contract conflict 2026-06-01 18:57:46 +00:00
Jeppe B 787db994dd Fix property gate command authorization bypass 2026-06-01 20:57:21 +02:00
Jeppe B f8f603a38e Merge pull request #175 from copenhagentruckwash/fix-vulnerability-in-studio-graph-edits
Fix authorization boundary for studio graph lane operations
2026-06-01 20:56:51 +02:00
Jeppe B 31a7224272 Fix studio graph lane operations permission checks 2026-06-01 20:56:38 +02:00
Jeppe B 492c81e27c Merge pull request #174 from copenhagentruckwash/fix-vulnerability-in-studio-action-conditions
Fix fail-open condition gating in self-serve Studio action runner
2026-06-01 20:56:21 +02:00
Jeppe B 45bfb1525a Fix studio action conditions to fail closed without results 2026-06-01 20:56:04 +02:00
Jeppe B 71ffa20811 Validate booking order context before certificates 2026-06-01 20:55:26 +02:00
Jeppe B a466c6291c Prevent XL Vask list automation execution 2026-06-01 20:54:44 +02:00
Jeppe B 9606d3b11d Merge pull request #171 from copenhagentruckwash/fix-sensitive-data-exposure-vulnerability
Remove committed replication bootstrap snapshot with secrets
2026-06-01 20:54:25 +02:00
Jeppe B 03b7fcd1b1 Remove committed replication bootstrap snapshot 2026-06-01 20:54:10 +02:00
Jeppe B 3d0f0f3391 Merge pull request #170 from copenhagentruckwash/fix-hard-coded-bearer-token-in-tests
Remove committed bearer token from invoicing HTTP example
2026-06-01 20:53:55 +02:00
Jeppe B e3257465a0 Remove hard-coded bearer token from invoicing HTTP example 2026-06-01 20:53:42 +02:00
Jeppe B 0c21f6e3a1 Merge pull request #169 from copenhagentruckwash/fix-gateway-auto-provision-deployment-vulnerability
Pin gateway auto-provision deployments to source commit
2026-06-01 20:53:22 +02:00
Jeppe B f1e5cacd0c Pin gateway auto-provision deployments to source commit 2026-06-01 20:53:10 +02:00
Jeppe B a8d5320ae5 Merge pull request #168 from copenhagentruckwash/fix-auto-promotion-vulnerability-in-release-gate
Prevent auto-sync promotion when release gate `required_checks` is empty
2026-06-01 20:52:52 +02:00
Jeppe B 95ac0d3a2c Block release gate auto-sync when required checks are empty 2026-06-01 20:52:39 +02:00
Jeppe B 1ed27dd467 Merge pull request #167 from copenhagentruckwash/fix-superuser-invite-resend-security-flaw
Scope superuser subuser invite resends
2026-06-01 20:52:23 +02:00
Jeppe B c4bb7bbb8b Scope superuser subuser invite resends 2026-06-01 20:52:08 +02:00
Jeppe B c09b7ebe76 Merge pull request #166 from copenhagentruckwash/fix-pathoutcomespayload-argument-type-error
Accept null confirmation rows in pathOutcomesPayload
2026-06-01 19:56:09 +02:00
Jeppe B 166ed6b92b Merge pull request #165 from copenhagentruckwash/fix-self-serve-invoice-assignment-issue
Fix self-serve invoice customer attribution
2026-06-01 19:54:28 +02:00
Jeppe B 8e528f3eae Fix null path confirmation rows 2026-06-01 19:53:40 +02:00
copilot-swe-agent[bot] 160772b832 Merge origin/master and resolve invoice billing test conflict 2026-06-01 17:52:21 +00:00
Jeppe B c8a5c3969d Fix self-serve invoice customer attribution 2026-06-01 19:48:10 +02:00
Jeppe B bb98df9e73 Merge pull request #164 from copenhagentruckwash/fix-truckwash-edge-gateway-stack.service-errors
Fix edge gateway PHP Docker extension setup
2026-06-01 19:30:15 +02:00
Jeppe B fe3719530a Fix edge gateway PHP image extensions 2026-06-01 19:19:01 +02:00
Jeppe B 603f497bef Merge pull request #163 from copenhagentruckwash/investigate-test-failure-issues
ci: retry Release Manager gate on transient 504s
2026-06-01 17:09:13 +02:00
Jeppe B ee16db8ecc ci: retry release manager gate on transient failures 2026-06-01 16:56:34 +02:00
Jeppe B c5c33d3cf7 Merge pull request #162 from copenhagentruckwash/fix-missing-happy-path-coverage-marker
Restore selected orders API coverage
2026-06-01 16:41:45 +02:00
Jeppe B da05c5adb7 Restore selected orders API coverage 2026-06-01 16:31:06 +02:00
Jeppe B 707cf67d5c Remove OrdersApiTest to clean up obsolete test cases 2026-06-01 13:07:21 +02:00
Jeppe B 09fa186028 Merge pull request #161 from copenhagentruckwash/codex/master-tests-pass-api-20260528
[codex] Fix backend master test gates
2026-05-29 16:32:31 +02:00
Jeppe B 5e6b340f8c Use compose broker URL for edge gateway smoke 2026-05-29 15:29:41 +02:00
Jeppe B 04e47a2e6d Start all PHP upstreams for edge gateway smoke 2026-05-29 15:10:49 +02:00
Jeppe B 572f5027d6 Run edge gateway smoke inside compose network 2026-05-29 14:56:36 +02:00
Jeppe B 235e0268c2 Fix backend CI gate failures 2026-05-29 14:36:18 +02:00
Jeppe B 65d639853b Skip Qodana when cloud token is unavailable 2026-05-28 23:44:07 +02:00
Jeppe B e856bbffec Trigger backend master test gates 2026-05-28 23:35:12 +02:00
Jeppe Bundgaard 3ee5b789ce Update setMachineRelayStatusHard method to use MACHINE_PROGRAM_PICKER constant for relay status setting 2026-05-28 21:08:37 +02:00
Jeppe Bundgaard 7f5722ff75 Add exception handling for cleaner relay activation in self-serve lanes
- Include `\Throwable` in docstring for better error documentation.
- Implement `turnOnCleanerRelayForWashStart` in the wash start process.
2026-05-28 20:40:08 +02:00
Jeppe B 50b596af39 Merge pull request #157 from copenhagentruckwash/fix-issues-and-verify-with-tests
Fix test gateway Windows config paths
2026-05-28 19:39:51 +02:00
Jeppe B af06c4d81e Merge pull request #160 from copenhagentruckwash/copilot/fix-qodana-workflow-failure
Fix Qodana failure on self-hosted runner by trusting workspace as Git safe.directory
2026-05-28 19:39:25 +02:00
Jeppe B 41ed692299 Merge pull request #159 from copenhagentruckwash/fix-subuser-token-permission-bypass
Restrict replication endpoints to classic users
2026-05-28 19:37:54 +02:00
copilot-swe-agent[bot] 31214f0af0 fix: mark workspace as git safe directory before qodana 2026-05-28 17:34:58 +00:00
Jeppe B aceaa6b957 Fix Qodana workflow and Windows-style test gateway paths
Update the Qodana workflow to use an available action version and avoid cloud-token failures when the secret is absent. Keep the test gateway path resolver using Windows path semantics for Windows-style inputs.
2026-05-28 19:33:02 +02:00
copilot-swe-agent[bot] cd0e0f0e61 Initial plan 2026-05-28 17:30:45 +00:00
copilot-swe-agent[bot] 0db6b5269d Merge origin/master and resolve replication route conflict 2026-05-28 17:29:12 +00:00
Jeppe B 3fb1eb9644 Restrict replication endpoints to classic users 2026-05-28 19:25:59 +02:00
Jeppe B 76dfcd70d1 Merge pull request #158 from copenhagentruckwash/fix-authorization-bypass-in-self-serve-lanes
Harden self-serve lane mutation authorization
2026-05-28 19:25:01 +02:00
Jeppe B b13abe0d30 Harden self-serve lane mutation authorization 2026-05-28 19:23:31 +02:00
Jeppe B 270e5b970f Support Windows-style test gateway paths
Resolve test gateway paths with the Windows path implementation when inputs use Windows-style syntax. This preserves the existing runnable script test suite without adding Windows-only tests.
2026-05-28 19:16:52 +02:00
Jeppe B 5dac3211ff Fix test gateway Windows config paths
### Motivation
- Tests that resolve the test gateway config directory were failing on Windows-style paths because the code always used the POSIX `path` module, producing mismatched separators.
- Preserve Windows path semantics when `rootDir` or an explicit config path uses Windows syntax while leaving POSIX behavior unchanged.

### Description
- Add `usesWindowsPathSyntax` and `pathForInputs` helpers to detect Windows-style paths and select `path.win32` when needed.
- Use the selected `pathModule` in `resolveConfigDirectory` to call `resolve`/`join` so Windows roots or explicit Windows dirs keep correct separators.
- Change is confined to `scripts/test-gateway.mjs` and does not alter other runtime behavior.

### Testing
- Ran `node --test scripts/*.test.mjs` which initially showed one failing path test and after the fix completed with all tests passing (`14` passed, `0` failed).
- Ran `npm test` in `services/edge-agent` and `services/edge-broker`, both suites passed (`18` and `23` tests respectively).
- Ran `node scripts/sync-ai-workflow.mjs --check` and `git diff --check` which both succeeded.
2026-05-28 19:11:58 +02:00
Jeppe B 4d91fc8ead Fix test gateway Windows config paths 2026-05-28 19:00:31 +02:00
Jeppe B 893ed1bda5 Fix PHP CI legacy and edge gateway tests
- Match self-serve legacy test double invoice signature.
- Wait for the edge gateway integration database before bootstrapping schema.
2026-05-28 18:03:30 +02:00
Jeppe Bundgaard 90ebec84bf Add PHP CI test script and optimize Redis config in tests
- Introduced a PHP CI test script for managing test suites.
- Consolidated Redis configuration retrieval.
- Optimized test fixture queries with dynamic object type assignments.
2026-05-28 17:58:06 +02:00
Jeppe Bundgaard bdf2a787d6 Merge remote-tracking branch 'origin/master' 2026-05-28 17:33:10 +02:00
Jeppe Bundgaard f8c254607d Implement Lane Status Audit and Comprehensive Self-Serve API Enhancements
- Introduced `machine_status_audit` in self-serve lanes for tracking changes.
- Added new methods to handle audit data including `setLaneStatusAudit` and `getMachineStatusAudit`.
- Enhanced API tests to include legacy Redis constant checks and validated comprehensive self-serve invoice creation.
- Updated department lanes to reflect audit logs in their responses.
2026-05-28 17:27:25 +02:00
Jeppe B 20eb92891a Avoid empty self-serve invoice orders
Only create the invoice order context when elapsed minute billing has a positive quantity. This preserves automatic-mode included-minute reduction without leaving an empty order id on the lane.

Tests:
- bash scripts/php-ci-test.sh unit
2026-05-28 17:21:36 +02:00
Jeppe Bundgaard 4cfe906f55 Update invoice function in selfserve_lane_command_t to accept command arguments and add necessary requires in selfserve_lane_invoice_t. 2026-05-28 16:36:09 +02:00
Jeppe Bundgaard 184ea1ca6c Enhance invoice and self-serve logic with subuser support
- Add subuser ID management to `selfserve_lane_command_arguments`.
- Update `invoice` function to include optional command arguments.
- Attach metadata to orders with self-serve and subuser details.
- Introduce `OTHER_TYPE_SELF_SERVE_WASH` in `attachment_content`.
2026-05-28 16:11:14 +02:00
Jeppe Bundgaard ae3657e7aa Add new API tests for order item note requirements, subuser route updates, and department lane status management
- Introduced tests for validating note requirements on order items.
- Updated subuser route management contract tests with new route coverage.
- Added endpoints to manage department lane and self-serve lane statuses, with associated tests.
2026-05-28 16:06:14 +02:00
Jeppe Bundgaard 54de2e5674 Add fake classes for relay logic and refactor relay shutdown without pre-checking status
Introduce helper classes `SelfserveWashCompletionRelayValueFake`, `SelfserveWashCompletionDepartmentLaneFake`, `SelfserveWashCompletionRelayLaneFake`, and `SelfserveWashCompletionFlowHarness` to simulate relay logic for unit tests. Refactor `turnOffRelayIfConfiguredAndOn` to `turnOffRelayIfConfigured`, removing relay status pre-check for cleaner and machine relays when completing a wash session, and test associated relay actions.
2026-05-27 19:30:31 +02:00
Jeppe Bundgaard eef436d44b Add tests for subuser password validation and grant permission normalization
Introduce unit and API tests for subuser password policies ensuring compliance with complexity requirements. Normalize subuser grant permission handling for consistency, including support for legacy zero permissions.
2026-05-27 19:17:19 +02:00
Jeppe Bundgaard b7aeb11801 Add department_selfserve_path_confirmations table and enhance PingApiTest
Introduce a new database table `department_selfserve_path_confirmations` to store path confirmations related to department configurations. Update `PingApiTest` to verify additional keys, ensuring `backend_version` and `api_commit_sha` are checked in the response.
2026-05-27 17:35:16 +02:00
Jeppe Bundgaard d52ceb8513 Add robust release update and API health checks
This commit introduces a release update mechanism, including candidate detection, asset pre-downloading, and installation workflows with proper state management. Additionally, it implements API health checks both for successful and failure scenarios and adds related unit and e2e tests for enhanced reliability.
2026-05-27 13:24:15 +02:00
Jeppe Bundgaard 1504f1b116 Add tests for ReleaseManager's handling of production database targets, service set statuses, data target detection, and beta release bundle policies. 2026-05-26 17:28:12 +02:00
Jeppe Bundgaard 78462f3ae4 Integrate cors_policy class to standardize CORS handling, refactor optionsRoute to use it, and add unit tests for CORS and Self-Serve Lane Access functionalities. 2026-05-26 16:35:02 +02:00
Jeppe Bundgaard 6ff6ce9b48 Add tests for automatic path-routed release target preparation and application target handling failures in ReleaseManager 2026-05-26 15:11:27 +02:00
Jeppe Bundgaard bc7c0280f2 Add .gitattributes for binary files and extend order booking update tests. Enhance dynamic image and routing logic with program_picker support. 2026-05-26 14:04:27 +02:00
Jeppe Bundgaard f1c123a840 Add tests to ensure order PO defaults from booking when missing and enhance existing routing logic. 2026-05-21 11:35:01 +02:00
Jeppe Bundgaard e40c6b6bac Probe release gateway health by channel path 2026-05-20 19:49:21 +02:00
Jeppe Bundgaard 8a1ec91b9e Allow explicit runtime channel selection 2026-05-20 19:39:23 +02:00
Jeppe Bundgaard f7ff9f0a3b Use Dockerfile builds for frontend release targets 2026-05-20 18:53:41 +02:00
Jeppe Bundgaard 16cfcaf41f Route frontend release targets through gateway 2026-05-20 18:07:23 +02:00
Jeppe Bundgaard 86fb092c22 Add extensive testing for ReleaseManager status overview logic and normalize API ingress paths. Enhance CORS headers and update gateway route URL handling for Coolify. 2026-05-20 17:49:16 +02:00
Jeppe Bundgaard c5a1271798 Enhance Coolify API deployment with improved gateway route handling and extensive test coverage. Add new methods for service updates and ensure Composer vendor sanity checks in PHP container. 2026-05-20 16:45:50 +02:00
Jeppe Bundgaard 5ea12f5342 Add Coolify API deployment image 2026-05-20 15:27:20 +02:00
Jeppe Bundgaard 44c4b7656f Enhance Coolify integration with gateway route deployment, add tests for new application route labels, and refactor gateway probing process. 2026-05-20 12:59:51 +02:00
Jeppe Bundgaard 24ac681365 Add tests for GitHub commit timestamp handling and runtime channel selection in ReleaseManager. Extend release URL normalization and runtime channel methods, and introduce assignment subject searches. 2026-05-20 11:27:29 +02:00
Jeppe Bundgaard c2263b8c98 Add tests for Coolify app payload handling, e-conomic customer fields, and expand Coolify API client capabilities 2026-05-19 16:55:45 +02:00
Jeppe Bundgaard cbf3c2d2b9 Add tests and methods for enhanced Coolify app handling, include isolated stack support and schema updates 2026-05-19 14:45:45 +02:00
Jeppe Bundgaard 00a8723347 Integrate Coolify API client and module for managing Coolify services, enhancing automation and deployment processes. 2026-05-19 13:17:07 +02:00
Jeppe Bundgaard ab31cd6dbb Enhance MinIO handling in replication management and update legacy test bootstrap. Add MinIO replication logic, legacy setup cleanup, and include necessary tests for improved MinIO interaction and error tolerance. 2026-05-18 14:12:03 +02:00
Jeppe Bundgaard 3261ed8414 Refactor employee name handling to utilize workfeed_employee_name_formatter for improved name resolution and fallback logic 2026-05-18 10:59:23 +02:00
Jeppe Bundgaard f53b99ad94 Implement replication management endpoints and enhance application write freeze handling 2026-05-18 09:59:15 +02:00
Jeppe Bundgaard 0399cb4bb4 Implement economic config round-trip test and enhance department handling
- Added a test to ensure correct round-tripping of default distribution department config value through economic config updates.
- Improved department handling by adding fallback logic to use the default economic distribution department id when a customer's department id is missing.
- Enhanced weather API routes to fetch, cache, and return detailed employee contributions per department for a given time slot.
2026-05-13 17:37:49 +02:00
Jeppe Bundgaard 30ed01e717 Refactor transaction handling and improve shift time logic
Introduced a new `getAllTransactionIds` utility function to better handle filtering of transaction IDs. Replaced outdated `start`/`end` time properties with `checkIn`/`checkOut` objects for shift records, alongside added validation in tests to exclude shifts without punches. Enhanced invoicing tests to ensure flags remain visible even for excluded transactions.
2026-05-13 12:19:07 +02:00
Jeppe Bundgaard 5965c5d72d Implement invoice period warming queue handling with Redis interface
- Added methods `enqueueInvoicePeriodWarming` and `consumeInvoicePeriodWarmingQueue` to the `Redis` interface for managing warming periods.
- Modified `invoice_period_flag_service` to enqueue warming periods on cache misses.
- Updated cron job logic to process invoice period warming queues and ensure flags are warmed effectively.
2026-05-12 16:01:55 +02:00
Jeppe Bundgaard 5f36939833 Refactor error handling for invoice period flag and item row fetching, improve cURL timeout
- Simplify error handling in `invoice_period_flag_service` by directly returning empty arrays on exceptions, removing redundant cache warm-up logic.
- Increase `CURLOPT_TIMEOUT` to 30 in `economic_m.php` for more reliable network requests.
- Adjust unit tests to reflect updated cURL timeout value.
2026-05-12 15:30:25 +02:00
Jeppe Bundgaard c343b52b57 Improve cache handling with on-demand cache warm-up and increase cURL timeout
- Implement on-demand warming of manual and automatic flags cache in `invoice_period_flag_service` to handle cache misses effectively.
- Extend cURL timeout in `economic_endpoint_t` for improved reliability in network requests.
2026-05-12 15:12:59 +02:00
Jeppe Bundgaard d9813a3fe2 Add caching methods for invoice period flags and cron jobs for warming caches
- Introduced methods for caching, retrieving, and clearing manual and automatic invoice period flags, as well as order item rows, using the Redis interface.
- Implemented `warmManualFlagsCache` and `warmAutomaticFlagsForPeriod` methods in `invoice_period_flag_service` to enhance performance by loading flags and order items into cache.
- Added new cron jobs `WarmInvoicePeriodManualFlagsCron` and `WarmInvoicePeriodAutomaticFlagsCron` to regularly update cached data for improved access speeds.
2026-05-12 14:04:51 +02:00
Jeppe Bundgaard c8aba05bc1 Add dynamic COMPOSE_PROJECT_NAME and container naming conventions to CI workflows
- Updated `tests.yml` to set `COMPOSE_PROJECT_NAME` dynamically based on `github.run_id` and `github.run_attempt`.
- Updated `docker-compose.ci.yml` to use dynamic container names with `COMPOSE_PROJECT_NAME`.
2026-05-12 05:18:32 +02:00
Jeppe Bundgaard 08ecd237b4 Escape ${} syntax in GitHub Actions debug database configuration to prevent variable interpolation issues. 2026-05-12 04:53:09 +02:00
Jeppe Bundgaard 9d608bc967 Add debug database configuration to GitHub Actions workflows for improved testing 2026-05-12 04:37:23 +02:00
Jeppe Bundgaard dbc195c31d Remove edge-broker service binding from Traefik configuration files 2026-05-12 04:30:55 +02:00
Jeppe Bundgaard aaae6c9536 Handle null connection and suppress exceptions in DB close method 2026-05-12 04:21:45 +02:00
Jeppe Bundgaard 6c40810caf Add unit tests for invoicing period pagination, normalization, and filtering logic
- Implemented `InvoicingPeriodPaginationTest` for testing period pagination modes, normalization of options, search functionality, and visibility filters.
- Added comprehensive tests to validate scenarios such as active period views, exact counts, and customer-card level search.
- Improved cURL timeout settings with `CURLOPT_CONNECTTIMEOUT` and `CURLOPT_TIMEOUT` adjustments.
- Introduced and documented helper classes/methods for local caching, pagination response structure, and customer name retrieval.
2026-05-12 03:37:47 +02:00
Jeppe Bundgaard 70080086da Add unit tests for Redis namespace safety, MotorAPI cache functionality, and configuration classes, alongside implementation of xlvask_automation_service
- Added tests to ensure Redis namespace safety for `db_object_t` and `users_o`.
- Implemented `MotorApiCachedResultTest` to validate metadata caching behavior.
- Introduced configuration classes for `xlvask_automatic_order_attachment_enabled` and `xlvask_automatic_order_creation_enabled`.
- Developed `xlvask_automation_service` with supporting features for usage log evaluation, suggestion building, and order automation.
2026-05-12 00:22:27 +02:00
Jeppe Bundgaard c1b66a81cc Add invoice_period_flag_ classes to manage invoice period flags with schema, services, and flag lifecycle methods
- Introduced `invoice_period_flag_schema_bootstrap` to initialize the schema for invoice period flags.
- Added `invoice_period_flag_service` to handle manual and automatic flag creation, updates, filtering, and context resolution.
- Implemented lifecycle methods such as `createManualFlag`, `updateAutomaticFlagStatus`, and `applyFlagsToPeriodTypes` for handling invoice period flags and their usage in processing periods.
- Included context-specific resolution methods for efficient flag management in invoicing workflows.
2026-05-11 21:34:57 +02:00
Jeppe Bundgaard 6d4066be1c Add unit tests for InvoicingPeriodDraftOverlay and reference suggestion logic, including fake DB integration and aggregation methods
- Implemented `InvoicingPeriodDraftOverlayTest` with coverage for blocking and permitting invoicing actions based on draft states, transactions, and metadata.
- Created `ReferenceSuggestionsApiTest` to validate ranked and filtered suggestions across bookings, orders, and vehicles with varied match relevance, context, and frequency.
- Added `order_reference_suggestions_service` class, including query methods, normalization utilities, and aggregation logic for reference suggestions.
- Enhanced query handling in `InvoicingPeriodDraftOverlayFakeDb` to validate SQL constraints and column cache resets in overlapping invoicing contexts.
2026-05-11 18:18:08 +02:00
Jeppe Bundgaard bea7e5697b Handle empty inputs in Redis and database operations, improve safety seal validation, and enhance related tests
- Return empty arrays for empty inputs in Redis `mget`, `db_object_t`, and `users_o` operations.
- Refactor safety seal validation logic to handle numeric strings and improve clarity.
- Add unit and API tests to verify handling of empty inputs and numeric safety seal strings.
2026-05-11 04:36:11 +02:00
Jeppe Bundgaard 59a6297925 Refactor booking completion logic to improve wash certificate handling
- Replaced `orderWasCreatedDuringCompletion` flag with optimized checks for wash certificate attachment.
- Updated method signatures to use nullable `safety_seal` parameter for consistency.
- Enhanced `completeBooking` logic to prevent duplicate wash certificate creation or sending.
- Added `hasWashCertificateAttached` method to streamline order checks and improve clarity.
- Updated tests to cover edge cases for wash certificate attachment and email dispatch behavior.
2026-05-11 01:56:46 +02:00
Jeppe Bundgaard 0cbc3e9aa5 Add support for archived departments with schema updates, API integration, and filtering logic
- Added `archived` column and index to `departments` table, ensuring schema initialization via `departments_schema_bootstrap`.
- Updated OpenAPI spec to include `archived` attribute and `filters=archived` query parameter with superuser access control.
- Enhanced `Departments` API to support archived department filtering and retrieval.
- Modified `ApiFixtures`, `departments_o`, and related tests to validate behavior for archived departments.
- Added unit and API tests to ensure correct handling of archived departments and filter enforceability.
2026-05-07 13:50:32 +02:00
Jeppe Bundgaard a4c2b4e95a Add BrandingApiTest to validate branding CRUD operations, permissions, and department assignments. 2026-05-07 10:44:15 +02:00
Jeppe Bundgaard 22fcb5cd0e - Refactor machine_1 drawing logic: optimize highlighted button rendering and deferred processing.
- Add branding management feature: API routes, payload handling, and OpenAPI schema updates.
- Implement department branding logic: CRUD operations, validation, and permissions.
- Add order deletion confirmation support with conflict handling and OpenAPI schema updates.
- Enhance tests and API methods for improved order handling and branding workflows.
2026-05-07 10:44:00 +02:00
Jeppe Bundgaard a71bde3211 Remove legacy booking completion forms and related logic
- Deleted `complete_booking_f` and `generate_booking_wash_certificate_f` classes.
- Updated tests to ensure legacy booking completion routes are disabled.
- Introduced tests for POST `/order-bookings/complete` to enforce POS-based booking completion management.
- Added `/collected-invoices/split-by-month` route with API and unit tests for splitting collections into monthly periods.
- Refactored impacted files to exclude legacy references and ensure continued compatibility with POS processes.
2026-05-06 14:02:48 +02:00
Jeppe Bundgaard 8bf957b273 Add support for selfserve_enabled lanes and synchronize behavior across tasks, sessions, and projections
- Introduced `selfserve_enabled` property for `department_lanes` with schema update, object properties, and associated methods/tests.
- Enhanced `selfserve_wash_flow` and session logic to respect lane self-serve settings, including block handling and task filtering.
- Updated API routes and Studio Graph projections to include `selfserve_enabled` in payloads and progress callbacks.
- Added unit tests for session statuses, lane configuration, and blocking behavior due to disabled self-serve settings.
2026-04-29 17:28:42 +02:00
Jeppe Bundgaard 5875371d13 Add attachment payload handling and tests for self-serve tasks
- Introduced `selfserve_task_attachment_payloads` class for managing task attachments, including formatting and download URL generation.
- Added unit and API tests to validate attachment handling in self-serve tasks and customer-scoped workflows.
- Enhanced wash start simulation and studio graph projections to integrate task attachment data.
2026-04-29 16:03:03 +02:00
Jeppe Bundgaard 93cac644a1 Introduce selfserve_studio_action_runner and related classes for configurable Studio action workflows
- Added `selfserve_studio_action_runner` to manage Studio action execution, including conditional validation, retry mechanisms, and operation dispatching.
- Introduced `selfserve_studio_actions` to define action constants, normalize configurations, and validate operations and policies.
- Updated `selfserve_config_versioning` to support action nodes, including validation hooks, schema migration normalization, and legacy action parsing.
- Enhanced `SelfserveStudioGraphTest` and `SelfserveStudioDebugPayload` tests to validate action serialization and runtime signal processing.
- Added test cases for event-driven Studio actions and non-blocking configuration warnings.
2026-04-29 10:57:33 +02:00
Jeppe Bundgaard eeccacb2a7 Add unit tests for department lane dynamic image overrides and introduce classes for self-serve signal and virtual hardware management
- Add `DepartmentLaneDynamicImageRouteTest` to verify dynamic image preview handling for studio lanes.
- Introduce `selfserve_machine_signal` class to standardize signal normalization, recording, and gateway signal management workflows.
- Add `selfserve_virtual_hardware` class to handle virtual hardware configurations, including gateway and binding management.
- Enhance structure with auxiliary methods for payload normalization, workspace merging, and validation warnings.
2026-04-29 09:52:51 +02:00
Jeppe Bundgaard 690e114d38 Add tests for customer-scoped vehicle conditions and property gate permissions
- Introduced tests for `SelfserveNonOwnedVehicleWashAccess` to validate customer-scoped conditions for non-owned vehicles.
- Added `SelfservePropertyGatePermissionBypassTest` to ensure proper permission handling for lanes and departments.
- Updated `department_selfserve_vehicle_conditions_o` and routes to prevent cross-customer answer persistence.
- Enhanced `selfserve_wash_flow` with customer-scoped persisted answer logic and improved method parameters for vehicle eligibility and session synchronization.
- Adjusted OpenAPI spec and unit tests to reflect new customer-scoping behavior in self-serve operations.
2026-04-28 17:59:33 +02:00
Jeppe Bundgaard 4dd00cd7a2 Add tests for handling ambiguous timeout errors and deferred relay side effects in Self-serve entrance start logic
- Introduced `SelfserveLaneStartEntranceTimeoutHarness` class and supporting tests to validate ambiguous relay timeout handling during entrance operations.
- Added `defer_relay_side_effects` parameter to `selfserve_lane_command_arguments` for improved relay control during wash start.
- Enhanced lane start routine to support conditional relay side effects and timeout handling with detailed logging.
2026-04-28 17:10:04 +02:00
Jeppe Bundgaard acdff75311 Add requestBooleanFlag helper and enhance session synchronization logic
- Introduce `requestBooleanFlag` method for consistent boolean parameter handling with default values.
- Add `activate_machine` and `sync_relay_state` parameters to `synchronizeSession` for more flexible relay and machine activation control.
- Update methods, routes, and tests to integrate the new session synchronization parameters effectively.
- Enhance debugging support with additional metadata in simulation and payload captures.
2026-04-28 16:54:57 +02:00
Jeppe Bundgaard 5b94c9407b Add service and role properties to task and binding nodes in Self-serve Studio Graph tests 2026-04-28 15:42:19 +02:00
Jeppe Bundgaard 206b487fa2 Add new table and enhance studio layout logic
Introduce `department_selfserve_studio_layouts` table for department-specific layouts and implement advanced auto-layout functionality in the DepartmentSelfServeStudio module. Added custom node definitions, updated styling, and integrated new logics for sorting and visualizing nodes in the Vue Flow interface.
2026-04-28 14:21:31 +02:00
Jeppe Bundgaard 72df2244ca Add self-serve API fixtures and enhance session synchronization logic
- Implement `createSelfServeScenario` to generate comprehensive self-serve test fixtures, including departments, lanes, tasks, and sessions.
- Add `syncRelayState` parameter to `synchronizeSession` for decoupled relay hardware synchronization.
- Update relevant routes and tests to reflect changes in session synchronization methods.
- Enhance Shelly request handling by blocking real device interactions in test mode with detailed logging.
2026-04-28 12:28:29 +02:00
Jeppe Bundgaard 55f8f25fd7 Add broker diagnostics and enhance relay logging
- Implement `diagnoseBrokerConfiguration` to validate broker URLs, shared secrets, and connection health.
- Add diagnostic methods for shared secret validation, including legacy sync support.
- Extend relay logging with descriptive context (`relay_name`, `relay_role`) and dynamic messaging.
- Update tests to cover broker health and shared secret diagnostics.
2026-04-28 11:30:09 +02:00
Jeppe Bundgaard c73e459d26 Add edge gateway broker configurations and session management routes
- Add broker-related configuration classes (`broker_url`, `public_broker_url`, `auth_mode`, `shared_secret`) to support edge gateway functionality.
- Enhance `SelfserveRoute` with routes for managing self-serve wash sessions, including session listing, detail retrieval, and forced lane stop.
- Update unit tests to validate new configuration handling, session routes, and OpenAPI endpoint coverage.
- Include default environment variables for broker settings in `docker-compose.example.yml`.
2026-04-28 10:04:17 +02:00
Jeppe Bundgaard 2aded0812a Add new tests for shell bridge and broker to handle structured failures and invalid session handling
- Add tests for shell bridge to validate structured error reporting on spawn failures.

- Add broker tests to ensure proper rejection of malformed browser shell upgrades without leaking sensitive tokens.

- Update `.env.example` with `EDGE_PUBLIC_BROKER_URL` for public access configuration.
2026-04-28 09:00:12 +02:00
Jeppe Bundgaard e93d3f30d2 Add support for Shelly device generation detection and update related tests
- Implement generation detection logic for Shelly devices using model codes, metadata, and type inference.
- Extend relay switch and inventory handling to include generation capabilities.
- Ensure compatibility with Gen1, Gen2, and Gen3 devices for relay control and diagnostics.
- Update unit tests to validate generation inference, fallback behavior, and API compatibility.
2026-04-27 17:40:39 +02:00
Jeppe Bundgaard ae5cb7c65f Add toggle_after timer support for relay switches and update unit tests
- Extend relay switch logic to include `toggle_after` parameter for timed toggles.
- Update unit tests in `SelfserveLanePortControllerTest` and `SelfserveRouteWiringTest` to validate timer behavior.
- Adjust Shelly API calls and assertions to handle timer values in both RPC and legacy endpoints.
2026-04-27 17:15:53 +02:00
Jeppe Bundgaard feb9aac4f7 Handle relay command job timeouts for edge gateways
- Introduce `expireTimedOutRelayStatusCommandJobs` to clean up long-pending relay status command jobs.
- Add `TIMED_OUT` status for relay command jobs and incorporate it into job status evaluations.
- Refactor command job finalization to support timeout-specific error messaging.
- Improve handling of fast-path failures in edge broker commands.
2026-04-27 16:47:09 +02:00
Jeppe Bundgaard 86eec9a51e Add timer support for relay switch commands and update tests
- Introduce `dispatchRelaySwitchWithTimer` and `dispatchRelaySwitchLocalOnlyWithTimer` methods for timed relay control.
- Extend `dispatchRelaySwitchWithOptions` to handle `toggleAfterSeconds` parameter.
- Update tests to validate timer functionality for local Shelly APIs.
- Ensure backwards compatibility with legacy APIs and adjust payloads accordingly.
2026-04-27 16:19:14 +02:00
Jeppe Bundgaard f2e2b9a8f4 Update relay-handling logic and test assertions for device binding and local IP resolution
- Correct test cases to ensure proper relay IDs are switched.
- Add robust local IP resolution for relay-device bindings, including caching and inventory backfill.
- Introduce fast-path options for relay status and switch dispatch.
- Validate PHP extensions (`curl`, `sqlite3`) in edge agent images.
2026-04-27 16:02:36 +02:00
Jeppe Bundgaard 7bd940de37 Improve browser-shell session closure handling in edge-broker tests and server 2026-04-27 12:15:46 +02:00
Jeppe Bundgaard c95fd3a23c Add fallback handling for telemetry ingestion failures in broker and HTTP persistence check for control plane events 2026-04-27 11:52:38 +02:00
Jeppe Bundgaard 99a85878cd Handle orphaned edge gateways for non-existent departments and improve error handling 2026-04-27 10:07:48 +02:00
Jeppe Bundgaard 1dffbe16c4 Restore self-hosted CI runners 2026-04-24 21:19:05 +02:00
Jeppe Bundgaard ad9637ebd2 Run CI on GitHub-hosted runners 2026-04-24 21:17:22 +02:00
Jeppe Bundgaard 0253cfc676 Copy edge E2E runner config in CI 2026-04-24 21:11:08 +02:00
Jeppe Bundgaard 15250ad227 Run edge E2E smoke on compose network 2026-04-24 21:04:53 +02:00
Jeppe Bundgaard ca78e8e5f3 Use internal API route for edge E2E in CI 2026-04-24 20:52:29 +02:00
Jeppe Bundgaard ab8923bb77 Attach CI runner to edge E2E network 2026-04-24 20:47:18 +02:00
Jeppe Bundgaard 0f37bdc288 Resolve fixed-name Traefik in E2E smoke 2026-04-24 20:36:09 +02:00
Jeppe Bundgaard cdbc976b8a Use Traefik container IP for edge E2E 2026-04-24 20:31:55 +02:00
Jeppe Bundgaard 288627243b Resolve edge E2E host routing in CI 2026-04-24 20:27:44 +02:00
Jeppe Bundgaard ebe1299089 Fix PHP unit test isolation 2026-04-24 20:17:01 +02:00
Jeppe Bundgaard 3adf5957f0 Use Docker host gateway for edge E2E 2026-04-24 20:07:05 +02:00
Jeppe Bundgaard eba33d0735 Use CI volume for PHP app tests 2026-04-24 20:00:42 +02:00
Jeppe Bundgaard def45b1c23 Clean stale PHP files before CI sync 2026-04-24 19:56:13 +02:00
Jeppe Bundgaard 38481334c7 Sync PHP checkout into CI containers 2026-04-24 19:52:36 +02:00
Jeppe Bundgaard dce2207243 Pin compose file for edge gateway CI 2026-04-24 19:44:03 +02:00
Jeppe Bundgaard 5bfd2743be Use explicit edge gateway API test paths 2026-04-24 19:35:11 +02:00
Jeppe Bundgaard 38acbdefa0 Run edge gateway CI suites directly with Pest 2026-04-24 19:27:57 +02:00
Jeppe Bundgaard 64292e8761 Use composer run-script for edge gateway suites 2026-04-24 19:23:19 +02:00
Jeppe Bundgaard 5001cd1811 Install PHP dev dependencies before edge gateway CI tests 2026-04-24 19:21:23 +02:00
Jeppe Bundgaard cef53d8ebd Fix compose config test service parsing 2026-04-24 19:13:41 +02:00
Jeppe Bundgaard ca64f2b047 Merge remote-tracking branch 'origin/run-tests-and-fix-identified-issues' 2026-04-24 19:10:55 +02:00
Jeppe Bundgaard ed13f7a8d4 Add guarded legacy route shims for Edge Gateway with corresponding unit tests 2026-04-24 15:51:09 +02:00
Jeppe B 2b905df478 Fix Caddy compose mounts for self-hosted Docker runtime 2026-04-24 00:19:13 +02:00
Jeppe B d9ed1ff9de Harden self-hosted CI workflow reliability 2026-04-24 00:10:10 +02:00
Jeppe B 1c647bb78b Avoid Traefik port collisions in edge gateway CI 2026-04-23 23:52:01 +02:00
Jeppe B 39d4ba0284 Run unit CI inside php1 container on self-hosted runner 2026-04-23 23:40:12 +02:00
Jeppe B 8a5e6bc302 Prepare Qodana temp directories on self-hosted runner 2026-04-23 23:35:13 +02:00
Jeppe B e7220f4f29 Run Qodana workflow on self-hosted runner 2026-04-23 23:16:45 +02:00
Jeppe B 54bada14f5 Fix edge agent shell polling and heartbeat metadata 2026-04-23 23:13:44 +02:00
Jeppe Bundgaard 2258a609e6 Enhance edge gateway logs handling and fix shell socket newline character formatting 2026-04-23 22:05:02 +02:00
Jeppe Bundgaard b98cef0caa Increase operation timeout, add diagnostic logs, and enhance gateway stream summarization 2026-04-23 21:36:25 +02:00
Jeppe Bundgaard 0813bfc0f0 Add customer mass import service with API route, test coverage, and e-conomic integration 2026-04-23 21:07:21 +02:00
Jeppe Bundgaard 4420d76cd2 Add "stage" field and additional JSON payloads to Edge Gateway operation events schema 2026-04-23 15:50:30 +02:00
Jeppe Bundgaard b9ddc585db Add Edge Gateway tests for unit, API, and integration scenarios 2026-04-23 15:48:51 +02:00
Jeppe Bundgaard c38a4379bd Add department hardware workspace and plate scanner updates 2026-04-23 13:31:57 +02:00
Jeppe Bundgaard e09e23025d Add Edge Broker service and rename 'rows' column to 'terminal_rows' in Edge Gateway schema 2026-04-22 19:31:21 +02:00
Jeppe Bundgaard 4c32eae49a Remove deprecated Edge Gateway Agent classes and related services 2026-04-22 19:08:44 +02:00
Jeppe Bundgaard 0d3c2d70bf Add auto-updater service with Docker integration, stack dependencies, and heartbeat management 2026-04-22 15:49:21 +02:00
Jeppe Bundgaard fd830deda9 Implement stack health diagnostics, timeout adjustments, and cleanup dependencies 2026-04-22 15:00:12 +02:00
Jeppe Bundgaard f4952f16e6 Add caching layer for edge gateway views, including payload storage, retrieval, sync, and invalidation 2026-04-22 14:23:24 +02:00
Jeppe Bundgaard 300942f1c8 Merge branch 'backend-coolify-runner' 2026-04-22 12:05:04 +02:00
Jeppe Bundgaard 9cfb19a3ba Replace CI PHP suite execution script with Composer commands and integrate Edge Gateway Agent stack artifacts 2026-04-22 12:03:08 +02:00
Jeppe B 6925b47a5b Merge pull request #153 from copenhagentruckwash/backend-coolify-runner
Backend coolify runner
2026-04-21 22:19:21 +02:00
Jeppe Bundgaard a842edea97 Sync PHP app into php1 on CI runner 2026-04-21 22:11:06 +02:00
Jeppe Bundgaard d30393b609 Run PHP suites in php1 on CI 2026-04-21 22:00:03 +02:00
Jeppe Bundgaard 7c7895f776 Restore edge agent shell polling 2026-04-21 21:46:39 +02:00
Jeppe Bundgaard e4fefab576 Run PHP jobs in setup-php container image 2026-04-21 21:36:06 +02:00
Jeppe Bundgaard 837c49a589 Configure setup-php for self-hosted runner 2026-04-21 21:31:18 +02:00
Jeppe Bundgaard 98b3943919 Restore edge broker compose defaults 2026-04-21 21:23:49 +02:00
Jeppe Bundgaard 5f83042317 Use self-hosted runner for backend tests 2026-04-21 20:05:10 +02:00
Jeppe Bundgaard 0b18433fdf Add unit tests for EconomicCustomerModel parsing and validation 2026-04-21 15:07:41 +02:00
Jeppe Bundgaard 7d450e285e Remove outdated edge gateway object classes, add new agent implementation
Transitioned from obsolete gateway object classes (`edge_gateway_shell_action_jobs_o`, `edge_gateway_shell_events_o`, `edge_gateway_shell_sessions_o`, `edge_gateway_update_jobs_o`) to the new agent implementation (`edge-gateway-agent/agent.php`).
2026-04-21 14:13:17 +02:00
Jeppe Bundgaard d30a006457 Add delivery metadata support, preferred channels, and enhanced agent validation
This commit introduces delivery metadata tracking for gateway commands, updates, and shells. Adds preferred delivery channel handling, refined validation for edge agents, improved relay management logic, and broker presence reporting. Includes schema changes, enhanced shell handling, and test coverage.
2026-04-16 13:44:28 +02:00
Jeppe Bundgaard d4d162d38a Update api-server.err.log to include extended PHP server activity and error instances indicating "address already in use" issues. 2026-04-16 11:03:29 +02:00
Jeppe B 218cf10ee3 Merge pull request #152 from copenhagentruckwash/edge-and-workers
Add robust edge agent and gateway update handling mechanisms
2026-04-15 13:44:12 +02:00
Jeppe Bundgaard b6454e5d36 Add edge agent update handling and verification mechanisms
Refactored the edge agent to include robust update handling, verification, and rollback procedures. Enhanced test coverage for critical update flows and introduced support for background edge gateway refresh without disrupting current user actions.
2026-04-15 12:07:32 +02:00
Jeppe Bundgaard 56685d7bf3 Add tests and functionality for edge gateway updates and lifecycle
This commit introduces unit tests, E2E tests, and implementation updates related to edge gateway lifecycle management, including update handling, artifact validation, and rollback mechanisms. It also refines routing, component interaction, and backend methods to improve update tracking, status transitions, and artifact management.
2026-04-15 11:53:45 +02:00
Jeppe Bundgaard 40ded9ed95 Add tests for API fixture cleanup and optimize customer trace removal logic 2026-04-15 09:17:46 +02:00
Jeppe Bundgaard 0b672e13a6 Validate and sanitize Composer vendor directory in PHP service's entrypoint script 2026-04-14 19:33:25 +02:00
Jeppe Bundgaard 39d745e079 Remove ws module from edge-agent dependencies 2026-04-14 19:33:07 +02:00
Jeppe Bundgaard c23bfb6d2f Update workerRoute version to 1.0.2 2026-04-14 18:58:47 +02:00
Jeppe Bundgaard d7296e67d9 Remove unnecessary test file tetststsssss.txt 2026-04-14 17:17:06 +02:00
Jeppe Bundgaard 12205eefe8 Add compose environment secrets handling to CI workflow 2026-04-14 16:58:41 +02:00
Jeppe Bundgaard 33fbf6d90d Add initial docker-compose.yml with service definitions for Traefik, Redis, MySQL, Edge Broker, Caddy, and supporting components in local and staging environments 2026-04-14 16:55:20 +02:00
Jeppe Bundgaard b21d0ce708 Validate compose contracts in CI and enhance edge-broker tests with a unified file reading utility 2026-04-14 16:54:10 +02:00
Jeppe Bundgaard 7a4463c1b3 Add Dockerfile for Edge Broker service 2026-04-14 16:48:57 +02:00
Jeppe Bundgaard 69eefdd572 Add Edge Broker service and configuration defaults
Introduced the Edge Broker service in the Docker Compose setup, configured to run on port 4300. Updated `.env.example` to include default environment variables for the Edge Broker and adjusted tests to validate the new configuration. Updated visual snapshot tests to reflect related UI changes.
2026-04-14 16:48:41 +02:00
Jeppe Bundgaard e3f771a36c Refactor edge-broker tests to consolidate file reading logic and streamline assertions 2026-04-14 16:41:18 +02:00
Jeppe Bundgaard 5c51601c62 Add waitFor to ensure shell session close callback in edge-broker test 2026-04-14 16:38:37 +02:00
Jeppe Bundgaard a1bde3f51e Add waitFor utility to edge-broker tests for reliable condition polling and update session close assertion 2026-04-14 16:36:27 +02:00
Jeppe Bundgaard 17d855d04f Refactor edge agent test with waitFor utility and update GitHub Actions workflow
- Introduced `waitFor` utility in edge agent tests for more reliable condition polling.
- Adjusted test assertions to use `waitFor` for verifying agent polling activity.
- Added separate GitHub Actions jobs for `edge-agent` and `edge-broker` to improve test isolation.
2026-04-14 16:31:47 +02:00
Jeppe Bundgaard f51b054cf5 Update GitHub Actions to conditionally upload coverage artifacts for pull requests 2026-04-14 16:15:52 +02:00
Jeppe Bundgaard e385cd3e95 Add xlvask_vehicle_types table and refine API test assertion
- Added schema creation logic for `xlvask_vehicle_types` table in `ApiSchemaBootstrap`.
- Updated `OrdersApiTest` to generalize HTTP error message assertion.
2026-04-14 16:12:57 +02:00
Jeppe Bundgaard a7328809c8 Add table and column existence checks in schema bootstrap and enhance API fixtures
- Added `tableExists` and updated `columnExists` in schema bootstrap to avoid redundant queries.
- Enhanced `ApiFixtures` to cast the `processor` attribute as an integer.
- Introduced `products` table creation in `ApiSchemaBootstrap`.
2026-04-14 16:10:12 +02:00
Jeppe Bundgaard 074ec3ac36 Add supporting tables initialization in schema bootstrap
- Added `ensureSupportingTables` method to create `departments`, `department_variables`, and `users` tables if they do not exist.
- Updated `department_daily_report_complaints_schema_bootstrap` to include supporting tables setup for schema consistency.
2026-04-14 16:03:18 +02:00
Jeppe Bundgaard 55a5ce453a Add column existence checks in schema bootstrap to prevent redundant ALTER queries
- Introduced `columnExists` helper to verify column presence before performing ALTER operations.
- Updated schema bootstrap logic to use `columnExists` for `wash_date` and `category` columns.
2026-04-14 15:57:26 +02:00
Jeppe Bundgaard 2ba7451669 Add schema bootstrap for API tests and update GitHub Actions environment setup
- Introduced `ApiSchemaBootstrap` class to ensure database schema for API tests.
- Modified GitHub Actions workflow to include schema bootstrapping configuration.
- Switched to Xdebug for test coverage in workflows and adjusted `ini-values` accordingly.
- Improved environment variable handling in `config.php` for enhanced runtime flexibility.
2026-04-14 15:44:50 +02:00
Jeppe Bundgaard 0fcb38b607 Enable PCOV coverage by adding pcov.enabled=1 in GitHub Actions workflow 2026-04-14 14:46:12 +02:00
Jeppe Bundgaard 07867fd811 Update test coverage script and extend GitHub Actions test environment
- Refactored `test:coverage` command in `composer.json` to support both PCOV and Xdebug coverage drivers, with fallback error handling for missing drivers.
- Expanded environment variables in GitHub Actions workflow to support application configuration and testing scenarios.
2026-04-14 14:37:00 +02:00
Jeppe Bundgaard 9a3844d57c Remove unused branch specification from GitHub Actions workflow 2026-04-14 14:27:22 +02:00
Jeppe Bundgaard d3239bb64e Add WebSocket implementation with support for extensions and event handling 2026-04-14 14:19:29 +02:00
Jeppe Bundgaard 02df12cb81 Add Node-API utility scripts and configuration files.
- Introduced new tools for N-API module detection (`check-napi.js`), formatting using Clang and ESLint (`clang-format.js`, `eslint-format.js`), and source conversion (`conversion.js`).
- Added N-API-specific configuration files (`common.gypi`, `except.gypi`).
- Included N-API bindings and licensing files (`index.js`, `LICENSE.md`, `napi.h`).
2026-04-14 13:45:41 +02:00
Jeppe Bundgaard ff2d9788bf Remove dependency on the ws module.
Deleted the entire `ws` library and its associated files. This likely reflects a move away from the WebSocket library, possibly signaling an alternate implementation or unused/legacy code cleanup.
2026-04-14 13:45:17 +02:00
Jeppe Bundgaard ebf7e820d5 Add safety seal support to orders and related logic for wash certificates
- Introduced `safety_seal` column in the `orders` table.
- Updated order creation and completion logic to handle safety seal values.
- Enhanced order and booking classes to manage safety seal attachment and retrieval.
- Added tests to validate safety seal functionality in order processing.
2026-04-14 10:51:25 +02:00
Jeppe Bundgaard 98f3188a98 Add API tests for Vehicles endpoint to validate last_order_id logic 2026-04-13 21:42:48 +02:00
Jeppe Bundgaard dc79352f40 Add unit tests for vehicle search metadata alignment and extend API fixtures for vehicle and order item creation. Refactor booking and route handling logic for booked vehicle searches. 2026-04-13 21:42:37 +02:00
Jeppe Bundgaard d267fb0f09 Expand Bird Voice Webhook tests to cover scenarios with shared gates and distinct entrance/exit gates, refine payload validation, and enhance gate resolution logic. 2026-04-13 17:55:53 +02:00
Jeppe Bundgaard 8465ee794d Add API test suite for Bird Voice Webhook endpoints, including comprehensive lifecycle tests for inbound call handling scenarios. Extend fixtures with department gate creation support and update OpenAPI spec validations. 2026-04-13 17:08:37 +02:00
Jeppe Bundgaard 2a8e8986e6 Add API testing framework and initial test cases for authentication endpoints 2026-04-13 11:50:10 +02:00
Jeppe Bundgaard 92d79b867a Remove ws package and all associated files from edge-agent. 2026-04-13 10:06:07 +02:00
Jeppe Bundgaard a83b850aba Remove edge_broker_client.php and related unit tests. Add new dependencies and configuration files for edge-agent, including build scripts and package-lock updates. 2026-04-13 09:42:35 +02:00
Jeppe Bundgaard 16c9cd9931 Extend agent-cli with sync command, enhance schema validation for complaints, and add coverage for gateway command tests. 2026-04-09 16:31:58 +02:00
Jeppe Bundgaard 2a0a3468c1 Add dockerized fake-agent for integration testing and live edge-broker smoke tests. Introduce agent CLI status command and report helpers. Add browser shell session handling for disconnected agents. Extend tests and improve token validation flow. 2026-04-09 16:31:38 +02:00
Jeppe Bundgaard 745e68aa4f Add wash_date and category fields to complaints, update schema and OpenAPI spec, and extend unit and integration tests for validation and route handling. 2026-04-09 14:16:35 +02:00
Jeppe Bundgaard 22f856c62a Add unit tests for invoicing, orders normalization, gateway commands, and department complaints. Update schema bootstraps and improve agent command execution logic. 2026-04-09 12:24:22 +02:00
Jeppe Bundgaard 9e9372db05 Add unit tests for Bird department gate phone call logic, Edge Broker configuration, and gateway heartbeat status. Refactor warnings handling, Slack notifications, Bird client usage, and Edge gateway probes for improved maintainability and error clarity. 2026-04-09 08:53:10 +02:00
Jeppe Bundgaard 45b7250480 Add Edge Agent implementation for gateway connection lifecycle, agent commands, Shelly device discovery, relay control, and WebSocket communication with broker. Include unit tests for critical flows. 2026-04-08 19:01:42 +02:00
Jeppe Bundgaard 653680376a Add unit and integration tests for collected invoice queue handling, route hardening, lifecycle validation, and manual batch processing logic. 2026-04-08 15:53:22 +02:00
Jeppe Bundgaard c5cd42be7f Add unit and integration tests for economic_transfer_queue and related endpoints, replacing synchronous fallback methods with queue-based processing. 2026-04-08 12:29:57 +02:00
Jeppe Bundgaard ba23ad6e8f Add economic_transfer_executor and economic_transfer_queue classes for handling e-conomic invoice transfer logic, queue management, and processing. Include unit tests for Redis cache validation. 2026-04-08 11:20:08 +02:00
Jeppe Bundgaard 551161b692 Add unit tests for Bird webhook lifecycle, economic customer handling, and payment terms routes with improved error scenarios and response validation. Refactor configuration to support economic API fallback tokens. 2026-04-01 17:44:25 +02:00
Jeppe Bundgaard 368e03ce48 Remove redundant methods and IVR handling logic from Bird voice call webhook route, focusing on streamlined call flow and improved maintainability. 2026-03-27 11:15:27 +01:00
Jeppe Bundgaard 0dc1581eb4 Add unit tests for self-serve invoice billing logic, refactor minute-based billing calculations, and improve error handling for billable minutes adjustments. 2026-03-26 21:09:21 +01:00
Jeppe Bundgaard b42a1a69a0 Refactor and centralize Shelly rate-limiting logic in the shelly class, add global enforcement and unit tests, and remove redundant implementations in self-serve lane controllers. 2026-03-26 20:15:36 +01:00
Jeppe Bundgaard 0d825fb45a Refactor Bird voice call route with streamlined call flow, department options listing, and removed redundant methods. Adjust self-serve lane command invoicing logic for better sequence clarity. 2026-03-26 18:29:21 +01:00
Jeppe Bundgaard b0ed00cb0c Add exception annotation to openDepartmentGateForCommand method for clarity 2026-03-26 16:38:48 +01:00
Jeppe Bundgaard 2ba9c30b85 Add bird_payload classes for payload abstraction and normalization across flash and voice call routes, integrate with request validators, and add unit tests. 2026-03-26 15:56:49 +01:00
Jeppe Bundgaard b7fa5540e5 Update Bird class call logic: adjust polling interval and replace flash call creation with voice call creation 2026-03-26 15:29:18 +01:00
Jeppe Bundgaard fcf9924adc Add bird_flash_calls_client implementation with endpoint builder, request schemas, and validator for managing flash call functionality. 2026-03-26 15:18:55 +01:00
Jeppe Bundgaard a292c8a277 Enhance Bird class with flash call fallback logic, caller ID validation, and unit test coverage. Update gate call handling to prefer flash calls with fallback to regular calls. 2026-03-26 13:51:41 +01:00
Jeppe Bundgaard 6208f0ee1c Add flash call functionality to Bird class with support for gate flash call handling and unit tests. 2026-03-26 13:40:26 +01:00
Jeppe Bundgaard b4ef513ff4 Refactor hangup cause handling in Bird class and add new unit tests for call flow validation. 2026-03-26 13:22:35 +01:00
Jeppe Bundgaard 95063d2a70 Add Bird gate call flow unit tests, self-serve machine wash minutes config, relay sync improvements, and OpenAPI updates. Refactor Bird call handling with terminal status detection and timeout normalization. 2026-03-26 13:16:18 +01:00
Jeppe Bundgaard a4654398d0 Add dynamic_images_vehicle_type column to legacy self-serve schemas, update wash flow logic, and add unit tests for schema compatibility. 2026-03-26 11:58:12 +01:00
Jeppe Bundgaard f148b39a85 Add unit tests for legacy schema compatibility, property gate commands, lane state transitions, and relay synchronization. Extend relay logic with demo relay handling, dynamic image updates, phone normalization, and machine relay hard set methods. 2026-03-26 11:11:34 +01:00
Jeppe Bundgaard 76729f1b99 Add department self-serve config versioning routes with lifecycle actions (list, validate, publish, rollback) and associated logic for managing config versions. 2026-03-25 17:54:41 +01:00
Jeppe Bundgaard 482a17f093 Add unit tests for cleaner and machine relay flows, refactor relay logic with hard set methods, enhance route handling for lane state transitions, and update session management. 2026-03-25 15:08:50 +01:00
Jeppe Bundgaard 416eba7de5 Add unit tests and route updates for department self-serve enabled flag relay synchronization. Refactor relay handling with hard set methods and lane status guard bypass. 2026-03-25 14:24:30 +01:00
Jeppe Bundgaard 2a9ed5af0f Add unit tests for STOP command flow, including relay shutoff sequencing, vehicle-type product addition, and invoice handling. Refactor STOP logic to enable relay configuration checks, error handling, and session finalization without blocking. 2026-03-25 14:12:31 +01:00
Jeppe Bundgaard 4c81cfd0fe Add unit tests for lane relay Shelly batching, rate limit handling, and retry logic. Refactor Shelly relay state handling with snapshot caching, readiness checks, and reduced delay intervals. 2026-03-25 13:40:12 +01:00
Jeppe Bundgaard 5a29683fe8 Add MACHINE_PROGRAM_PICKER and MACHINE_CLEANER relay endpoints with GET/POST actions, unit tests, retry logic, and OpenAPI spec updates. 2026-03-25 12:21:47 +01:00
Jeppe Bundgaard 7fc70184df Add unit tests and routes for department weather targets, including GET/PUT endpoints, thresholds validation, and aggregate status handling. Extend OpenAPI spec with schema mappings for department weather targets and machine relay endpoints. 2026-03-25 11:14:25 +01:00
Jeppe Bundgaard 6fdc8d466e Add unit tests for various modules: attachments grouping, department weather caching behaviors, economic module order sanitization, enriched order batching, and user cashier name lookups. Update related route logic for enhanced data fetching and caching integrations. 2026-03-24 14:59:16 +01:00
Jeppe Bundgaard 4a5dd3a83f Add unit tests for department weather API: cache helpers, fallback behavior, timeline range handling, and schema conformance. Enhance route logic with custom date ranges, cache support, and fallback handling. Update OpenAPI spec and schema mappings. 2026-03-24 13:59:57 +01:00
Jeppe Bundgaard 228efb74fb Enhance department weather timeline API: support multiple IDs, add timeline date override, improve ID parsing, update schema examples, and expand related tests. 2026-03-24 13:06:40 +01:00
Jeppe Bundgaard c714af6c9e Add unit tests for Workfeed weather timeline range, schema conformance, and employee hours calculations. Enhance Workfeed API with improved department ID resolution, timeline range configuration, and workfeed shift handling. Update route logic to remove opening hours dependency and integrate Workfeed data. 2026-03-24 12:50:01 +01:00
Jeppe Bundgaard 28b571e2e6 Add unit tests for advanced target duration parsing, goal range calculations, and Workfeed query normalization. Extend OpenAPI spec with updated schemas and examples. 2026-03-24 11:35:13 +01:00
Jeppe Bundgaard 8c9388546b Add advanced target duration configurations and parsing logic to Goals module. Update Workfeed with new CompanyID config. Extend OpenAPI spec with detailed schema mappings and examples. 2026-03-24 11:23:08 +01:00
Jeppe Bundgaard 40171d9719 Update Workfeed OpenAPI spec with detailed schema definitions for employees, shifts, and departments. Add pagination support and replace generic response references with specific ones. 2026-03-24 10:37:43 +01:00
Jeppe Bundgaard 48a74ad34d Add Workfeed module with routes, configurations, and API integration handling for employees, shifts, and departments. Update OpenAPI spec and include unit tests for endpoint wiring and filtering logic. 2026-03-24 10:33:17 +01:00
Jeppe Bundgaard ac164a5e81 Add debug Redis configuration support, enhance Redis connection handling, and refactor worker status endpoint 2026-03-24 10:09:37 +01:00
Jeppe Bundgaard de89c4d635 Add database and Redis port configuration support, connection health checks, and refactor worker status endpoint. 2026-03-20 13:01:21 +01:00
Jeppe Bundgaard 219f739b54 Invalidate user permission and session caches when group permissions change; add tests. 2026-03-19 17:13:45 +01:00
Jeppe Bundgaard b547a8b029 Refactor permission handling to leverage standardized "forbidden" responses and enhance unit test coverage. 2026-03-19 15:59:25 +01:00
Jeppe Bundgaard 3752fdec4c Add support for "forbidden" response with missing permissions in Nginx response handler 2026-03-19 14:39:23 +01:00
Jeppe Bundgaard 60f6cbee84 Enhance CORS handling with dynamic origin validation and credentials support in Nginx. Update Traefik to include additional allowed origins. 2026-03-18 14:15:28 +01:00
Jeppe Bundgaard 6b928cfce7 Add PreRenderDynamicImagesCron for dynamic image variant caching and idempotency guard for order bookings. 2026-03-18 13:59:48 +01:00
Jeppe Bundgaard 90acf50b55 Add idempotency handling for order booking creation using Redis to prevent duplicate requests. 2026-03-18 13:42:47 +01:00
Jeppe Bundgaard da2cbee987 Disable sending welcome emails to info@truckwash.dk as requested by Christian. 2026-03-18 13:29:37 +01:00
Jeppe Bundgaard 7c55beae42 Refactor self-serve wash flow to decouple visible question logic, improve condition evaluation, and handle nullable fields in the OpenAPI spec. 2026-03-18 13:05:41 +01:00
Jeppe Bundgaard f04f79e3da Update Traefik configuration with enhanced routing rules, local IP allowlist for dashboard access, new strip prefix middleware, and extended CORS origins. Add ACME account configurations for certificate management. 2026-03-18 10:58:17 +01:00
Jeppe Bundgaard db42f9ef95 Add staging environment configuration for Caddy, Nginx, and Traefik with updated routing and logging settings 2026-03-18 10:32:41 +01:00
Jeppe Bundgaard b3772293f3 Add auto-generated documentation for new API endpoints: branding option, button press webhook, and customer attribute. 2026-03-17 16:51:10 +01:00
Jeppe Bundgaard 6cc5dc2210 Add initial Copenhagen Truck Wash API documentation, including API overview, authentication, error handling, introduction, and Writerside configuration. 2026-03-17 15:55:11 +01:00
Jeppe Bundgaard 979b0f8fac Add atomic reservation support in Redis for goal alert deduplication with expiration. Update Cron logic and add unit tests for validation. 2026-03-17 15:23:02 +01:00
Jeppe Bundgaard bd4a90cdaa Add bulk booked invoice line handling and optimized distribution logic for department 75. Update unit tests, Economic endpoint URL encoding, and service integration. 2026-03-17 14:53:20 +01:00
Jeppe Bundgaard 6aa04eb951 Add logic for booked department 75 redistribution with fixed pricing and subscription weights. Update OpenAPI spec, unit tests, and service integration. 2026-03-16 13:40:03 +01:00
Jeppe Bundgaard 425c2c9142 Update README to include self-serve module implementation and API guide location 2026-03-16 13:08:21 +01:00
Jeppe Bundgaard 2ecae070a2 Add documentation for the self-serve module, detailing architecture, flow, API routes, domain model, runtime tables, and eligibility rules. Includes end-to-end examples and PHP usage. 2026-03-16 13:06:17 +01:00
2309 changed files with 507860 additions and 12717 deletions
+223
View File
@@ -0,0 +1,223 @@
{
"version": 1,
"snapshot_of": "backend-php",
"projects": {
"backend-php": {
"display_name": "Copenhagen Truck Wash API",
"relative_root": ".",
"commands": {
"setup": {
"default": "./scripts/setup.sh",
"win32": "powershell -ExecutionPolicy Bypass -File .\\scripts\\setup.ps1"
},
"run": {
"default": "docker compose up -d traefik redis mysql-debug php1 caddy"
},
"test_unit": {
"default": "docker compose exec -T php1 sh -lc \"cd /var/www/html && composer test:unit\""
},
"test_e2e": {
"default": ""
},
"test_full": {
"default": ""
},
"debug": {
"default": "docker compose logs -f --tail=200 php1"
}
},
"codex_environment": {
"name": "api",
"actions": [
{
"name": "Start API",
"icon": "run",
"command_id": "run"
},
{
"name": "Stop API",
"icon": "run",
"literal_command": {
"default": "docker compose down"
}
},
{
"name": "PHP logs",
"icon": "debug",
"command_id": "debug"
},
{
"name": "PHP unit tests",
"icon": "test",
"command_id": "test_unit"
},
{
"name": "AI workflow check",
"icon": "debug",
"literal_command": {
"default": "node scripts/sync-ai-workflow.mjs --check"
}
}
]
},
"generated_content": {
"aiassistant_tests_lines": [
"# Backend PHP Testing Rules",
"",
"These rules apply to `services/nginx/app/tests` and any backend change that needs verification.",
"",
"1. Add or update tests for every new feature, bug fix, API contract change, or search or authentication workflow change.",
"2. Run backend verification in the `php1` container. Do not use host-side PHP for the supported workflow.",
"3. Use `docker compose exec -T php1 sh -lc \"cd /var/www/html && composer test:unit\"` for unit coverage.",
"4. Use `docker compose exec -T php1 sh -lc \"cd /var/www/html && composer test:integration\"` when code depends on Redis, MySQL, or environment-backed configuration.",
"5. Use `docker compose exec -T php1 sh -lc \"cd /var/www/html && composer test:api\"` when route behavior, envelopes, or request parsing changes.",
"6. Keep tests deterministic: no live third-party calls, no shared Redis keys, no broad database cleanup, and no sleeps unless time behavior is the thing under test.",
"7. Prefer narrow fixtures, explicit cleanup, and behavior-level assertions over implementation checks.",
"8. When public routes, schemas, or permissions change, update `openapi.yaml` together with the tests.",
"9. Run the narrowest relevant suite first, then the broader suite that matches the risk before you finish the task.",
"",
"Canonical workflow reference: `.ai-workflow/workflow.md`."
],
"aiassistant_routes_lines": [
"# Backend Route Rules",
"",
"These rules apply to files under `services/nginx/app/routes` and the classes they call.",
"",
"1. Keep route handlers thin: validate input, enforce permissions, call domain code, and write the response.",
"2. Default to protected endpoints. Use the existing authentication and permission helpers instead of ad hoc access checks.",
"3. Add or update backend tests in the `php1` container whenever route behavior changes.",
"4. Update `openapi.yaml` whenever paths, parameters, request bodies, response envelopes, or permissions change.",
"5. Prefer deterministic route tests and avoid live external integrations in route coverage.",
"6. Use concise API errors and keep sensitive implementation details out of the response body.",
"7. When a change touches department, order, or subuser authorization, cover both the allow path and the deny path.",
"",
"Canonical workflow reference: `.ai-workflow/workflow.md`."
],
"junie_lines": [
"# Copenhagen Truck Wash API Development Guidelines",
"",
"This file is generated from the canonical AI workflow and is the supported Junie-facing reference for the backend repository.",
"",
"## Build And Run",
"",
"- Project root: `services/nginx/app` is the effective PHP application root.",
"- Setup: use `./scripts/setup.sh` on POSIX or `powershell -ExecutionPolicy Bypass -File .\\scripts\\setup.ps1` on Windows.",
"- Start local API stack: `docker compose up -d traefik redis mysql-debug php1 caddy`.",
"- Tail logs with `docker compose logs -f --tail=200 php1`.",
"",
"## Testing",
"",
"- Supported backend validation runs in `php1`.",
"- Unit tests: `docker compose exec -T php1 sh -lc \"cd /var/www/html && composer test:unit\"`.",
"- Integration tests: `docker compose exec -T php1 sh -lc \"cd /var/www/html && composer test:integration\"`.",
"- API tests: `docker compose exec -T php1 sh -lc \"cd /var/www/html && composer test:api\"`.",
"- Prefer the narrowest suite that proves the change, then run the broader suite that matches the risk.",
"",
"## Workflow Notes",
"",
"- Generated assistant metadata is checked with `node scripts/sync-ai-workflow.mjs --check`.",
"- Route and schema changes require matching updates to `openapi.yaml`.",
"- Runtime OpenAI product behavior is out of scope for this workflow bundle unless a task explicitly changes product code.",
"",
"Canonical workflow reference: `.ai-workflow/workflow.md`."
]
}
}
},
"assistants": {
"codex": {
"description": "Codex environment files and actions."
},
"aiassistant": {
"description": "Project-specific AI Assistant rules."
},
"junie": {
"description": "Project-specific Junie guidance."
},
"copilot": {
"description": "Standardized Copilot task dispatch workflow."
}
},
"commands": {
"setup": {
"description": "Install dependencies and prepare the supported local environment."
},
"run": {
"description": "Start the primary local development entrypoint for the project."
},
"test_unit": {
"description": "Run the project's narrow unit-style verification command."
},
"test_e2e": {
"description": "Run the project's targeted browser or end-to-end verification command."
},
"test_full": {
"description": "Run the broader high-confidence verification command when the project defines one."
},
"debug": {
"description": "Run the supported debug entrypoint."
}
},
"generated_outputs": [
{
"id": "backend_codex_environment",
"template": "codex_project_environment",
"project": "backend-php",
"path": ".codex/environments/environment.toml"
},
{
"id": "backend_aiassistant_tests",
"template": "aiassistant_backend_tests_rule",
"project": "backend-php",
"path": ".aiassistant/rules/Creating and maintaining tests.md"
},
{
"id": "backend_aiassistant_routes",
"template": "aiassistant_backend_routes_rule",
"project": "backend-php",
"path": ".aiassistant/rules/Creating and securing routes.md"
},
{
"id": "backend_junie_guidelines",
"template": "junie_backend_guidelines",
"project": "backend-php",
"path": ".junie/guidelines.md"
},
{
"id": "backend_copilot_workflow",
"template": "copilot_dispatcher_workflow",
"project": "backend-php",
"path": ".github/workflows/copilot.yml"
}
],
"sync_targets": {
"backend-php": {
"source_root": ".",
"destination": "C:\\Users\\2jepp\\PhpstormProjects\\api",
"supported_metadata_dirs": [
".codex",
".aiassistant",
".junie",
".github",
".ai-workflow",
"scripts"
]
}
},
"copilot": {
"workflow_name": "Copilot Task Dispatcher",
"input_description": "The task description for Copilot",
"issue_label": "copilot-task",
"title_prefix": "Copilot Task:",
"body_lines": [
"Assigned to Copilot by @{{ACTOR}}.",
"",
"Task",
"{{TASK}}",
"",
"AI workflow notes",
"- Generated assistant metadata is synchronized from the canonical AI workflow bundle.",
"- Run `node scripts/sync-ai-workflow.mjs --check` if assistant metadata changed."
]
}
}
+74
View File
@@ -0,0 +1,74 @@
<!-- AUTOGENERATED SNAPSHOT for backend-php: refresh from the canonical workspace with `node scripts/sync-ai-workflow.mjs --write`. -->
# Developer AI Workflow
This directory is the canonical source of truth for the repository's developer-facing AI workflow.
## Goals
- Keep Codex, AI Assistant, Junie, and Copilot aligned from one maintained source.
- Make assistant metadata deterministic so the generated files can be rewritten safely and checked in CI.
- Preserve the current mirror-repo workflow for `backend-php` and `front-end-vue`.
- Keep all changes in this workflow scoped to developer tooling and documentation. Runtime OpenAI features stay out of scope.
## Supported Assistants
- `Codex`: local environments and actions under `.codex/environments`.
- `AI Assistant`: generated guidance under `.aiassistant/rules`.
- `Junie`: generated project guidance under `.junie/guidelines.md`.
- `Copilot`: standardized issue-dispatch workflow content under `.github/workflows/copilot.yml`.
## Global Rules
1. Change only `.ai-workflow/workflow.md` and `.ai-workflow/manifest.json` when updating the developer AI workflow.
2. Regenerate all derived files with `node scripts/sync-ai-workflow.mjs --write`.
3. Validate drift with `node scripts/sync-ai-workflow.mjs --check`.
4. Generated assistant files are not hand-edited.
5. Unsupported surfaces stay unsupported until they have a real owner and a real config.
6. `front-end-vue/.ai/mcp/mcp.json` is intentionally unsupported and should not be recreated until there is an actual MCP integration to maintain.
## Command Matrix
### `backend-php`
- Setup: use the existing setup scripts in `backend-php/scripts`.
- Run: start the API stack with `traefik`, `redis`, `mysql-debug`, `php1`, and `caddy`.
- Debug: tail `php1` logs.
- PHP verification: always run backend validation in the `php1` container.
- Unit tests: `docker compose exec -T php1 sh -lc "cd /var/www/html && composer test:unit"`.
- Integration tests: `docker compose exec -T php1 sh -lc "cd /var/www/html && composer test:integration"`.
- API tests: `docker compose exec -T php1 sh -lc "cd /var/www/html && composer test:api"`.
### `front-end-vue`
- Setup: `npm ci` and `npx playwright install`.
- Run: `npm run dev`.
- Unit tests: `npm run test:unit` with Vitest.
- Browser smoke: `npm run test:e2e:smoke`.
- Browser full suite: `npm run test:e2e:ci`.
- Debug: `npx playwright test --debug`.
- Browser validation is Playwright-first. WebdriverIO and Appium are not part of the supported workflow.
### `automation`
- Setup: `npm ci` and `npx playwright install`.
- Run: `npx playwright test --ui`.
- Browser tests: `npx playwright test`.
- Debug: `npx playwright test --debug`.
## Mirror Repos And Sync
- `backend-php` mirrors to `C:\Users\2jepp\PhpstormProjects\api`.
- `front-end-vue` mirrors to `C:\Users\2jepp\WebstormProjects\pleno-vue`.
- Supported metadata directories that must stay mirrored are `.codex`, `.aiassistant`, `.junie`, `.github`, generated `.ai-workflow`, and `scripts/sync-ai-workflow.mjs`.
- Cache and build directories remain excluded from the watcher.
- The mirror repos receive generated snapshots of `.ai-workflow` and `scripts/sync-ai-workflow.mjs` so their local CI can run `--check` without depending on the combined workspace root.
## Generated Outputs
- Root Codex environment for combined backend and frontend entrypoints.
- Backend and frontend Codex environments.
- Backend and frontend AI Assistant guidance.
- Backend and frontend Junie guidance.
- Backend Copilot dispatcher workflow.
- Backend and frontend snapshot copies of `.ai-workflow` plus `scripts/sync-ai-workflow.mjs` for mirrored repositories.
@@ -2,261 +2,20 @@
apply: always
---
### Creating and maintaining tests — Rules (Projectspecific)
<!-- AUTOGENERATED: Run `node scripts/sync-ai-workflow.mjs --write`. -->
These rules apply to all tests under `services/nginx/app/tests` for the Copenhagen Truck Wash API. They codify how to create, run, and maintain the lightweight CLI PHP tests used in this repository.
# Backend PHP Testing Rules
1. Scope and philosophy
- Prefer fast, deterministic CLI scripts over frameworkbased tests.
- Keep unitstyle tests isolated from I/O (no DB/Redis/files/network). Use test doubles and explicit `require_once` for only the code exercised.
- Use integration tests sparingly and only when configuration/globals are required. Run them in Docker with `USE_ENV=true`.
These rules apply to `services/nginx/app/tests` and any backend change that needs verification.
2. Location and naming
- Place tests in `services/nginx/app/tests/<domain>/YourTest.php` where `<domain>` reflects the feature area (e.g., `subusers`, `orders`, `redis`).
- File names must end with `Test.php` (e.g., `SelfservePermissionInitTest.php`).
- Keep one coherent scenario per file. If a file grows beyond ~150 lines or mixes unrelated scenarios, split it.
3. Execution modes
- Unitstyle (preferred): run on host with PHP CLI.
- Command: `php services/nginx/app/tests/<domain>/<Name>Test.php`
- Integrationstyle (needs env/globals): run inside Docker (php1) where `USE_ENV=true` and env vars are provided by `docker-compose.yml`.
- Start minimal stack: `docker compose up -d traefik redis php1 caddy`
- Command: `docker compose exec -T php1 php /var/www/html/tests/<domain>/<Name>Test.php`
4. Bootstrapping and includes
- At the top of every test, define `WD` if not already defined:
```php
if (!defined('WD')) { define('WD', dirname(__DIR__, 2)); }
```
- For unitstyle tests: include only the files you exercise via explicit `require_once WD . '/path/to/file.php';`.
- Do NOT include `config.php` for unitstyle tests; it throws if `USE_ENV` is not set.
- For integrationstyle tests that need globals (`$CONFIG_DB`, `$REDIS_CONFIG`, etc.): `require_once WD . '/config.php';` and run the test inside `php1`.
5. Output and exit codes
- Use simple helpers for humanreadable output:
```php
function ok($message){ echo "\n\033[32m✔ $message\033[0m\n"; }
function fail($message){ echo "\n\033[31m✖ $message\033[0m\n"; }
```
- Prefer collecting failures and exiting nonzero when any assertion fails:
```php
$failed = 0;
// ... on failure set $failed = 1;
exit($failed);
```
- End with a concise completion line, e.g., `echo "\n<Name>Test completed.\n";`.
6. Determinism, time, and randomness
- Avoid `sleep()` and real time dependencies. If time is relevant, pass it as a parameter or stub the time source.
- Do not use random values unless strictly necessary; if used, seed explicitly (`mt_srand(1234)`) and document it.
7. External I/O and side effects
- Never call external services (Stripe, economic, MinIO, Slack, WordPress) from tests. Stub or override code paths as shown in `tests/subusers/SelfservePermissionInitTest.php`.
- Do not modify files under the repository during tests. If temporary files are required, use `sys_get_temp_dir()` and delete them before exit.
8. Redis and database (integrationonly)
- Run inside Docker (`php1`) so `config.php` can populate `$REDIS_CONFIG` and `$CONFIG_DB`.
- Use unique, namespaced Redis keys for test data (e.g., `test:<feature>:<uuid>`). Clean them up at the end. Do NOT call `FLUSHALL`.
- Prefer fakes/stubs over real DB writes. If DB writes are unavoidable, scope them to clearly identifiable rows and delete them before exit.
9. Performance budgets
- Each unitstyle test script should complete in < 100 ms on a typical dev machine.
- Each integrationstyle test should complete in < 2 s and must avoid N+1 loops or heavy queries.
10. When to add or update tests
- New route/feature: add at least one unitstyle test for core logic. If behavior depends on configuration/globals, add a minimal integration test.
- Bug fix: first reproduce with a failing test; then fix and ensure the test passes.
- Refactor: keep behaviorpreserving tests green. If public behavior changes intentionally, update both tests and `openapi.yaml` accordingly.
11. Maintenance expectations
- Do not weaken or remove assertions to “make tests pass”. Investigate and fix root causes.
- Keep tests small and readable. Extract tiny helpers within the test file rather than introducing new shared libraries.
- Match the project code style and line endings (see `.editorconfig`: UTF8, CRLF, 4space indent, class brace `next_line`).
12. Templates
- Unitstyle template:
```php
<?php
if (!defined('WD')) { define('WD', dirname(__DIR__, 2)); }
// require_once WD . '/modules/foo/classes/bar.php';
function ok($m){ echo "\n\033[32m✔ $m\033[0m\n"; }
function fail($m){ echo "\n\033[31m✖ $m\033[0m\n"; }
$failed = 0;
// Arrange / Act
$sum = 2 + 2;
// Assert
if ($sum === 4) { ok('Basic arithmetic works (2 + 2 = 4)'); } else { fail('Expected 4'); $failed = 1; }
echo "\nExampleUnitTest completed.\n";
exit($failed);
```
- Integrationstyle template (Redis example):
```php
<?php
if (!defined('WD')) { define('WD', dirname(__DIR__, 2)); }
require_once WD . '/config.php'; // populates $REDIS_CONFIG when USE_ENV=true
require_once WD . '/classes/redis.php';
function ok($m){ echo "\n\033[32m✔ $m\033[0m\n"; }
function fail($m){ echo "\n\033[31m✖ $m\033[0m\n"; }
$failed = 0;
// Use a namespaced test key
$key = 'test:redis:example:' . uniqid('', true);
try {
$r = new classes\redis();
$r->set($key, 'pong');
$val = $r->get($key);
if ($val === 'pong') { ok('Redis set/get works for namespaced key'); } else { fail('Unexpected value from Redis'); $failed = 1; }
} finally {
// Besteffort cleanup
try { $r->del($key); } catch (\Throwable $e) { /* ignore */ }
}
echo "\nExampleRedisIntegrationTest completed.\n";
exit($failed);
```
13. Quick reference
- Run unitstyle test on host: `php services/nginx/app/tests/<domain>/<Name>Test.php`
- Run integrationstyle test in Docker: `docker compose exec -T php1 php /var/www/html/tests/<domain>/<Name>Test.php`
- Minimal stack for integration: `docker compose up -d traefik redis php1 caddy`
For broader build, configuration, and existing examples, see `.junie/guidelines.md` (Testing section).
---
apply: always
---
### Creating and maintaining tests — Rules (Projectspecific)
These rules apply to all tests under `services/nginx/app/tests` for the Copenhagen Truck Wash API. They codify how to create, run, and maintain the lightweight CLI PHP tests used in this repository.
1. Scope and philosophy
- Prefer fast, deterministic CLI scripts over frameworkbased tests.
- Keep unitstyle tests isolated from I/O (no DB/Redis/files/network). Use test doubles and explicit `require_once` for only the code exercised.
- Use integration tests sparingly and only when configuration/globals are required. Run them in Docker with `USE_ENV=true`.
2. Location and naming
- Place tests in `services/nginx/app/tests/<domain>/YourTest.php` where `<domain>` reflects the feature area (e.g., `subusers`, `orders`, `redis`).
- File names must end with `Test.php` (e.g., `SelfservePermissionInitTest.php`).
- Keep one coherent scenario per file. If a file grows beyond ~150 lines or mixes unrelated scenarios, split it.
3. Execution modes
- Unitstyle (preferred): run on host with PHP CLI.
- Command: `php services/nginx/app/tests/<domain>/<Name>Test.php`
- Integrationstyle (needs env/globals): run inside Docker (php1) where `USE_ENV=true` and env vars are provided by `docker-compose.yml`.
- Start minimal stack: `docker compose up -d traefik redis php1 caddy`
- Command: `docker compose exec -T php1 php /var/www/html/tests/<domain>/<Name>Test.php`
4. Bootstrapping and includes
- At the top of every test, define `WD` if not already defined:
```php
if (!defined('WD')) { define('WD', dirname(__DIR__, 2)); }
```
- For unitstyle tests: include only the files you exercise via explicit `require_once WD . '/path/to/file.php';`.
- Do NOT include `config.php` for unitstyle tests; it throws if `USE_ENV` is not set.
- For integrationstyle tests that need globals (`$CONFIG_DB`, `$REDIS_CONFIG`, etc.): `require_once WD . '/config.php';` and run the test inside `php1`.
5. Output and exit codes
- Use simple helpers for humanreadable output:
```php
function ok($message){ echo "\n\033[32m✔ $message\033[0m\n"; }
function fail($message){ echo "\n\033[31m✖ $message\033[0m\n"; }
```
- Prefer collecting failures and exiting nonzero when any assertion fails:
```php
$failed = 0;
// ... on failure set $failed = 1;
exit($failed);
```
- End with a concise completion line, e.g., `echo "\n<Name>Test completed.\n";`.
6. Determinism, time, and randomness
- Avoid `sleep()` and real time dependencies. If time is relevant, pass it as a parameter or stub the time source.
- Do not use random values unless strictly necessary; if used, seed explicitly (`mt_srand(1234)`) and document it.
7. External I/O and side effects
- Never call external services (Stripe, economic, MinIO, Slack, WordPress) from tests. Stub or override code paths as shown in `tests/subusers/SelfservePermissionInitTest.php`.
- Do not modify files under the repository during tests. If temporary files are required, use `sys_get_temp_dir()` and delete them before exit.
8. Redis and database (integrationonly)
- Run inside Docker (`php1`) so `config.php` can populate `$REDIS_CONFIG` and `$CONFIG_DB`.
- Use unique, namespaced Redis keys for test data (e.g., `test:<feature>:<uuid>`). Clean them up at the end. Do NOT call `FLUSHALL`.
- Prefer fakes/stubs over real DB writes. If DB writes are unavoidable, scope them to clearly identifiable rows and delete them before exit.
9. Performance budgets
- Each unitstyle test script should complete in < 100 ms on a typical dev machine.
- Each integrationstyle test should complete in < 2 s and must avoid N+1 loops or heavy queries.
10. When to add or update tests
- New route/feature: add at least one unitstyle test for core logic. If behavior depends on configuration/globals, add a minimal integration test.
- Bug fix: first reproduce with a failing test; then fix and ensure the test passes.
- Refactor: keep behaviorpreserving tests green. If public behavior changes intentionally, update both tests and `openapi.yaml` accordingly.
11. Maintenance expectations
- Do not weaken or remove assertions to “make tests pass”. Investigate and fix root causes.
- Keep tests small and readable. Extract tiny helpers within the test file rather than introducing new shared libraries.
- Match the project code style and line endings (see `.editorconfig`: UTF8, CRLF, 4space indent, class brace `next_line`).
12. Templates
- Unitstyle template:
```php
<?php
if (!defined('WD')) { define('WD', dirname(__DIR__, 2)); }
// require_once WD . '/modules/foo/classes/bar.php';
function ok($m){ echo "\n\033[32m✔ $m\033[0m\n"; }
function fail($m){ echo "\n\033[31m✖ $m\033[0m\n"; }
$failed = 0;
// Arrange / Act
$sum = 2 + 2;
// Assert
if ($sum === 4) { ok('Basic arithmetic works (2 + 2 = 4)'); } else { fail('Expected 4'); $failed = 1; }
echo "\nExampleUnitTest completed.\n";
exit($failed);
```
- Integrationstyle template (Redis example):
```php
<?php
if (!defined('WD')) { define('WD', dirname(__DIR__, 2)); }
require_once WD . '/config.php'; // populates $REDIS_CONFIG when USE_ENV=true
require_once WD . '/classes/redis.php';
function ok($m){ echo "\n\033[32m✔ $m\033[0m\n"; }
function fail($m){ echo "\n\033[31m✖ $m\033[0m\n"; }
$failed = 0;
// Use a namespaced test key
$key = 'test:redis:example:' . uniqid('', true);
try {
$r = new classes\redis();
$r->set($key, 'pong');
$val = $r->get($key);
if ($val === 'pong') { ok('Redis set/get works for namespaced key'); } else { fail('Unexpected value from Redis'); $failed = 1; }
} finally {
// Besteffort cleanup
try { $r->del($key); } catch (\Throwable $e) { /* ignore */ }
}
echo "\nExampleRedisIntegrationTest completed.\n";
exit($failed);
```
13. Quick reference
- Run unitstyle test on host: `php services/nginx/app/tests/<domain>/<Name>Test.php`
- Run integrationstyle test in Docker: `docker compose exec -T php1 php /var/www/html/tests/<domain>/<Name>Test.php`
- Minimal stack for integration: `docker compose up -d traefik redis php1 caddy`
For broader build, configuration, and existing examples, see `.junie/guidelines.md` (Testing section).
1. Add or update tests for every new feature, bug fix, API contract change, or search or authentication workflow change.
2. Run backend verification in the `php1` container. Do not use host-side PHP for the supported workflow.
3. Use `docker compose exec -T php1 sh -lc "cd /var/www/html && composer test:unit"` for unit coverage.
4. Use `docker compose exec -T php1 sh -lc "cd /var/www/html && composer test:integration"` when code depends on Redis, MySQL, or environment-backed configuration.
5. Use `docker compose exec -T php1 sh -lc "cd /var/www/html && composer test:api"` when route behavior, envelopes, or request parsing changes.
6. Keep tests deterministic: no live third-party calls, no shared Redis keys, no broad database cleanup, and no sleeps unless time behavior is the thing under test.
7. Prefer narrow fixtures, explicit cleanup, and behavior-level assertions over implementation checks.
8. When public routes, schemas, or permissions change, update `openapi.yaml` together with the tests.
9. Run the narrowest relevant suite first, then the broader suite that matches the risk before you finish the task.
Canonical workflow reference: `.ai-workflow/workflow.md`.
@@ -2,172 +2,18 @@
apply: always
---
### Creating and securing routes — Rules (Projectspecific)
<!-- AUTOGENERATED: Run `node scripts/sync-ai-workflow.mjs --write`. -->
These rules define how to add HTTP routes to the Copenhagen Truck Wash API and how to secure them consistently. They reflect current patterns in `services/nginx/app/routes` and the helper APIs in `traits\route_t`.
# Backend Route Rules
1. Location, naming, and structure
- Place route classes under `services/nginx/app/routes`.
- File names should describe the domain and end with `Route.php`, e.g., `BookingsRoute.php`, `ModuleSelfServeRoute.php`.
- Namespace must be `routes` and each file must define one class that uses `traits\route_t` and implements `run(): void`.
```php
<?php
namespace routes;
use traits\route_t;
These rules apply to files under `services/nginx/app/routes` and the classes they call.
class ExampleRoute
{
use route_t;
public function run(): void
{
// endpoints go here
}
}
```
2. Registering endpoints (HTTP verbs)
- Define endpoints inside `run()` using the helpers provided by `route_t`:
- `get($path, $handler, $permissions = [])`
- `post($path, $handler, $permissions = [])`
- `put($path, $handler, $permissions = [])`
- `delete($path, $handler, $permissions = [])`
- `patch($path, $handler, $permissions = [])`
- `options($path, $handler, $permissions = [])` (useful for CORS preflight when needed)
- Handlers are closures that perform validation, authorization, side effects, and write responses via the global `$response` (`classes\response`).
- Example (readonly endpoint):
```php
$this->get('/example', function () {
global $response;
$response->success(['message' => 'Hello World!']);
});
```
3. Route paths and local routing
- Caddy serves the app directly; Traefik adds an external `/api` prefix for local access.
- Direct path in the app: `/foo/bar`.
- Local access paths (both work):
- `http://localhost/foo/bar` (direct)
- `http://localhost/api/foo/bar` (Traefik stripprefix `/api` → still routes to `/foo/bar` in the app)
- Keep paths meaningful and resourceoriented. Prefer plural nouns for collections and subpaths for actions when unavoidable (e.g., `/modules/self-serve/lane/status`).
4. Responses and status codes
- Use `$response->success($payload, $status = 200)` for successful results and `$response->error($messageOrPayload, $status)` for failures.
- Typical statuses:
- 200 OK for successful reads/updates; 201 Created when new resources are created.
- 400 Bad Request for validation errors.
- 401 Unauthorized when authentication is missing/invalid.
- 403 Forbidden when authenticated but lacking permissions.
- Return concise, nonsensitive error messages. Internal details go to logs (see `objects\logs_o`).
5. Input handling and validation (route_t helpers)
- Read parameters via:
- `getParameter($name)` or `getParametersAsArray()` (preferred; integrates with `classes\response` parsing)
- `fromRequest($name)` (checks JSON body, then `$_POST`, then query)
- `fromQuery($name)` and `fromRoute($segmentName)` when relevant
- Validate early and fail fast using helpers:
- Presence: `requireParameters(['a','b'])`, `isParametersSet(['a','b'])`
- Types: `requireType($val, type_string()|type_int()|TYPE_BOOL()|TYPE_ARRAY())`, `requireTypeIn($val, [...])`
- Ranges/lengths: `requireMinValue($n, $min)`, `requireMaxValue($n, $max)`, `requireMaxLength($name, $len)`, `requireParameterIntPositive($n, $name)`
- Formats/enums: `requireDateFormat($date, FORMAT_DATE())`, `requireInArray($val, $allowed)`
6. Authentication and authorization (core rules)
- Default posture: endpoints are protected and require authentication unless explicitly public.
- To check authentication without a specific permission: `isAuthenticated()`.
- Enforce permissions inside handlers using:
- `requirePermission('some_permission')` — throws 401/403 as appropriate.
- `hasPermission('some_permission', $customerNumber = null)` — boolean check (do not throw).
- Register permissions for discoverability/administration by supplying the third `$permissions` argument when declaring the route. Keys are permission names; values are humanreadable descriptions. You should still call `requirePermission()` inside the handler to actually enforce.
```php
$this->get('/modules/self-serve/lane/status', function () {
global $response;
self::requirePermission('modules_selfserve_lane_status_view');
// ...
$response->success(['status' => 'OK']);
}, [
'modules_selfserve_lane_status_view' => 'View self-serve lane status',
]);
```
7. Subusers, permission nodes, and customer context
- When a permission corresponds to a subuser permission node, define it with `definePermission($perm, subusers_permission_node_key::CASE)` to keep mapping explicit.
- Subuser contexts may target a specific customer via the `X-Customer-Number` header or a `customer_number` parameter; `route_t` exposes this as response meta (`target_customer_number`) when applicable.
- Use `allowOwnOrDepartmentAccess($ownPerm, $deptPerm, $targetCustomerNumber, $departmentId, $ownGuard = null, $denyMessage = null)` for the common pattern: allow the principal to act on their own customer scope or fall back to a department/admin permission.
- Use `isOwnCustomerContext($targetCustomerNumber)` and `resolveEffectiveCustomerNumber()` when building conditional logic.
8. Department, order, and special access helpers
- Department access: `requireDepartmentAccess($departmentId, $permissionSuffix = null)`, `hasDepartmentAccess(...)`.
- Order access: `requireOrderAccess($orderId, $permissionSuffix = null)` (note: indirect access rules may evolve; keep logic minimal in routes).
- Plate scanners (API key based): protect endpoints with `requirePlateScannerAuth()`.
- Bot protection (public forms): use `requireRecaptcha()` and expect `g_recaptcha_response` in the request.
9. Public endpoints
- Keep public endpoints extremely limited. If an endpoint must be public, you must:
- Validate all inputs thoroughly using the helpers above.
- Add ratelimit or abusemitigation where relevant (Redis is available as `classes\redis` for counters/locks; coordinate design with maintainers before introducing new limits).
- Prefer `requireRecaptcha()` for anonymous form submissions.
10. Side effects and idempotency
- For POST/PUT/PATCH with side effects, make operations idempotent when feasible (e.g., by honoring a client idempotency key header). If you add such behavior, document it in the routes docblock and in `openapi.yaml`.
- Wrap multistep operations with proper validation and permission checks before any external calls (Stripe, economic, etc.). Do not leak thirdparty error payloads directly; map to concise API errors and log details.
11. Logging and observability
- On auth/permission denials, the helpers already log to `logs_o` with structured messages. Avoid duplicating logs for the same event.
- For unexpected states you handle gracefully, add targeted logs via `objects\logs_o` with a clear category/key.
12. OpenAPI contract synchronization
- Every public surface change must be reflected in `openapi.yaml` at repo root. Keep paths, methods, parameters, request/response schemas, and error statuses up to date.
- Use the same path strings as in the route declaration (remember Traefiks `/api` is stripped before hitting the app).
- If you add or change permissions that users must hold, document them in the endpoint description.
13. Example: secure command endpoint
```php
<?php
namespace routes;
use traits\route_t;
use classes\authentication;
class LaneCommandRoute
{
use route_t;
public function run(): void
{
$this->post('/modules/self-serve/lane/command', function () {
global $response;
// AuthZ
self::requirePermission('modules_selfserve_lane_command_execute');
// Validate input
self::requireParameters(['lane_id', 'command']);
$laneId = (int) self::getParameter('lane_id');
self::requireType($laneId, self::type_int());
self::requireMinValue($laneId, 1);
$cmd = (string) self::getParameter('command');
self::requireType($cmd, self::type_string());
self::requireInArray($cmd, ['START','STOP','RESERVE','RELEASE','RESET']);
// Business logic (call into modules/classes)
// ...
$response->success(['ok' => true]);
}, [
'modules_selfserve_lane_command_execute' => 'Execute selfserve lane commands',
]);
}
}
```
14. Quick reference (local)
- Bring up minimal stack: `docker compose up -d traefik redis php1 caddy`
- Access an app route locally:
- `http://localhost/<path>` or `http://localhost/api/<path>`
- Tests are CLI scripts under `services/nginx/app/tests`. Prefer unitstyle tests for core logic; see “Creating and maintaining tests — Rules”.
15. Code style and hygiene
- Follow `.editorconfig` (UTF8, CRLF, 4space indents).
- Keep route files cohesive; avoid adding unrelated endpoints to the same class. If a class exceeds ~200300 lines or mixes domains, split it.
- Do not perform heavy bootstrap in route files. Delegate to classes/modules and keep handlers thin: validate → authorize → invoke → respond.
1. Keep route handlers thin: validate input, enforce permissions, call domain code, and write the response.
2. Default to protected endpoints. Use the existing authentication and permission helpers instead of ad hoc access checks.
3. Add or update backend tests in the `php1` container whenever route behavior changes.
4. Update `openapi.yaml` whenever paths, parameters, request bodies, response envelopes, or permissions change.
5. Prefer deterministic route tests and avoid live external integrations in route coverage.
6. Use concise API errors and keep sensitive implementation details out of the response body.
7. When a change touches department, order, or subuser authorization, cover both the allow path and the deny path.
Canonical workflow reference: `.ai-workflow/workflow.md`.
+26 -11
View File
@@ -3,31 +3,46 @@ version = 1
name = "api"
[setup]
script = "./scripts/setup.sh"
script = '''
./scripts/setup.sh
'''
[setup.win32]
script = "powershell -ExecutionPolicy Bypass -File .\\scripts\\setup.ps1"
script = '''
powershell -ExecutionPolicy Bypass -File .\scripts\setup.ps1
'''
[[actions]]
name = "Start API"
icon = "run"
command = "powershell -ExecutionPolicy Bypass -File .\\scripts\\run.ps1 -Action start"
platform = "win32"
command = '''
docker compose up -d traefik redis mysql-debug php1 caddy
'''
[[actions]]
name = "Stop API"
icon = "run"
command = "powershell -ExecutionPolicy Bypass -File .\\scripts\\run.ps1 -Action stop"
platform = "win32"
command = '''
docker compose down
'''
[[actions]]
name = "PHP logs"
icon = "debug"
command = "powershell -ExecutionPolicy Bypass -File .\\scripts\\run.ps1 -Action logs -Service php1"
platform = "win32"
command = '''
docker compose logs -f --tail=200 php1
'''
[[actions]]
name = "Unit tests"
name = "PHP unit tests"
icon = "test"
command = "powershell -ExecutionPolicy Bypass -File .\\scripts\\run.ps1 -Action test"
platform = "win32"
command = '''
docker compose exec -T php1 sh -lc "cd /var/www/html && composer test:unit"
'''
[[actions]]
name = "AI workflow check"
icon = "debug"
command = '''
node scripts/sync-ai-workflow.mjs --check
'''
+7 -1
View File
@@ -1 +1,7 @@
/docker-compose.yml
/docker-compose.yml
# Runtime-generated replication bootstrap snapshots may contain infrastructure
# metadata and encrypted/plaintext credential material. They must be
# supplied at runtime via mounted storage, not baked into deployment images.
/services/nginx/app/storage/replication-bootstrap.json
/services/nginx/app/storage/replication-bootstrap-*.json
+32
View File
@@ -28,12 +28,44 @@ CONFIG_DB_HOST=
CONFIG_DB_USER=
CONFIG_DB_PASSWORD=
CONFIG_DB_DATABASE=
CONFIG_DB_PORT=3306
CONFIG_DB_SSL_MODE=DISABLED
# Browser origins allowed to call the API. Path-like entries are normalized to origins by the PHP CORS policy.
CORS=https://truckwash.io,https://www.truckwash.io,https://api.truckwash.io,https://api.truckwash.io:4433,https://api-v2.truckwash.io,https://web.truckwash.dk,https://api.truckwash.dk,https://truckwash.dk,https://www.truckwash.dk,https://staging.truckwash.io,http://localhost,https://localhost,http://localhost:4433,https://localhost:4433,https://twdev.jeppeb.dk,http://localhost:5173
# Debug DB credentials (used when CONFIG_DB_TARGET=debug)
# Any blank debug value falls back to the live value above.
CONFIG_DB_DEBUG_HOST=mysql-debug
CONFIG_DB_DEBUG_USER=root
CONFIG_DB_DEBUG_PASSWORD=debug_root_password
CONFIG_DB_DEBUG_DATABASE=nnks_db_debug
CONFIG_DB_DEBUG_PORT=3306
CONFIG_DB_DEBUG_SSL_MODE=DISABLED
# e-conomic credentials
# Required: ECONOMIC_API_APP_ACCESS_GRANT and ECONOMIC_API_APP_SECRET_TOKEN
# Optional-but-recommended: ECONOMIC_API_APP_ACCESS_GRANT2 (falls back to primary grant when blank)
ECONOMIC_API_APP_ACCESS_GRANT=
ECONOMIC_API_APP_ACCESS_GRANT2=
ECONOMIC_API_APP_SECRET_TOKEN=
# Edge broker defaults for shell relay and gateway dispatch.
EDGE_BROKER_URL=http://edge-broker:4300
EDGE_PUBLIC_BROKER_URL=http://localhost/api/edge-broker
EDGE_AUTH_MODE=strict
EDGE_BROKER_SHARED_SECRET=
# Redis credentials
REDIS_CONFIG_HOST=redis
REDIS_CONFIG_DATABASE=0
REDIS_CONFIG_PASSWORD=
REDIS_CONFIG_PORT=6379
REDIS_CONFIG_USER=default
# Redis debug credentials
REDIS_CONFIG_DEBUG_HOST=redis
REDIS_CONFIG_DEBUG_DATABASE=0
REDIS_CONFIG_DEBUG_PASSWORD=
REDIS_CONFIG_DEBUG_PORT=6379
REDIS_CONFIG_DEBUG_USER=default
+15
View File
@@ -0,0 +1,15 @@
* text=auto eol=lf
*.png binary
*.jpg binary
*.jpeg binary
*.gif binary
*.ico binary
*.webp binary
*.woff binary
*.woff2 binary
*.ttf binary
*.otf binary
*.pdf binary
*.zip binary
*.webm binary
+56
View File
@@ -0,0 +1,56 @@
# Default branch protection
`master` is changed through pull requests. Do not push or publish directly to
the default branch, including through automation or the Git Data API.
## Normal publishing flow
1. Create a scoped `agent/*` or feature branch from the current `origin/master`.
2. Commit and push only the intended changes.
3. Open a pull request targeting `master`.
4. Wait for the `Required CI` check. If `master` moves, update the branch and
wait for the strict check to rerun.
5. Resolve every review conversation and squash-merge the pull request.
6. Confirm the post-merge `Release Manager gate` completes on `master`.
The aggregate check covers the PHP unit, integration, API, and legacy matrix,
plus Edge Agent, Edge Broker, and Edge Gateway Backend. Qodana is advisory and
the Release Manager gate is intentionally post-merge.
## Desired ruleset
[`rulesets/protect-default-branch.json`](rulesets/protect-default-branch.json)
is the importable final desired-state repository-ruleset request body. For the
initial POST, copy the file and override `enforcement` to `disabled`. Inspect
the normalized ruleset and verify a green preparation PR and post-merge run,
then PUT the exact committed file to activate it.
The desired rule targets `~DEFAULT_BRANCH`, requires pull requests with zero
approvals, conversation resolution, strict `Required CI` from GitHub Actions
integration `15368`, squash-only linear history, and blocks deletion and force
pushes. Repository administrators receive pull-request-only bypass; they do not
receive a standing direct-push bypass.
When the ruleset is activated, align repository settings at the same time:
retain squash merging, disable merge commits and rebase merging, enable
auto-merge and branch-update suggestions, delete merged branches automatically,
keep the Actions token read-only, and prevent Actions from approving reviews.
## Activation record
Repository ruleset `19041620` was activated on 2026-07-16 after preparation
PR #311 passed `Required CI` and the merged `master` commit passed both
`Required CI` and the `Release Manager gate`. This documentation update is
the after-activation canary for the normal protected pull-request path.
## Break glass
When an incident cannot wait for the normal gate:
1. Open a pull request and describe the incident, risk, and reason for bypass.
2. Have a repository administrator use the pull-request-only bypass.
3. Monitor `Required CI` and the post-merge Release Manager workflow.
4. Open a follow-up pull request for any deferred validation or remediation.
Never bypass by updating `refs/heads/master` directly. Ruleset changes and
emergency bypasses must remain visible in GitHub's audit trail.
+48
View File
@@ -0,0 +1,48 @@
USE_ENV=true
DEBUG=true
ENCRYPTION_KEY=ci-test-encryption-key
CORS=*
CONFIG_TIMEZONE=Europe/Copenhagen
CONFIG_DB_TARGET=debug
CONFIG_DB_HOST=mysql-debug
CONFIG_DB_USER=root
CONFIG_DB_PASSWORD=debug_root_password
CONFIG_DB_DATABASE=nnks_db_debug
CONFIG_DB_PORT=3306
CONFIG_DB_SSL_MODE=DISABLED
CONFIG_DB_DEBUG_HOST=mysql-debug
CONFIG_DB_DEBUG_USER=root
CONFIG_DB_DEBUG_PASSWORD=debug_root_password
CONFIG_DB_DEBUG_DATABASE=nnks_db_debug
CONFIG_DB_DEBUG_PORT=3306
CONFIG_DB_DEBUG_SSL_MODE=DISABLED
REDIS_CONFIG_HOST=redis
REDIS_CONFIG_USER=default
REDIS_CONFIG_DATABASE=0
REDIS_CONFIG_PASSWORD=
REDIS_CONFIG_PORT=6379
REDIS_CONFIG_DEBUG_HOST=redis
REDIS_CONFIG_DEBUG_USER=default
REDIS_CONFIG_DEBUG_DATABASE=0
REDIS_CONFIG_DEBUG_PASSWORD=
REDIS_CONFIG_DEBUG_PORT=6379
ECONOMIC_API_APP_ACCESS_GRANT=ci-test
ECONOMIC_API_APP_ACCESS_GRANT2=ci-test-secondary
ECONOMIC_API_APP_SECRET_TOKEN=ci-test-secret
WORDPRESS_STATIC_TOKEN=ci-test
EMAIL_WASH_CERTIFICATE_TOKEN=ci-test
WORDPRESS_API_URL=http://localhost
MINIO_ENDPOINT=
MINIO_ACCESS_KEY=
MINIO_SECRET_KEY=
SLACK_DEFAULT_WEBHOOK=
EDGE_BROKER_URL=http://edge-broker:4300
EDGE_PUBLIC_BROKER_URL=http://localhost/api/edge-broker
EDGE_AUTH_MODE=manager
EDGE_BROKER_SHARED_SECRET=truckwash-edge-ci
EDGE_GATEWAY_VIEW_CACHE_TTL=0
TRUCKWASH_TEST_BLOCK_REAL_SHELLY=1
+48
View File
@@ -0,0 +1,48 @@
USE_ENV=true
DEBUG=true
ENCRYPTION_KEY=ci-test-encryption-key
CORS=*
CONFIG_TIMEZONE=Europe/Copenhagen
CONFIG_DB_TARGET=debug
CONFIG_DB_HOST=mysql-debug
CONFIG_DB_USER=root
CONFIG_DB_PASSWORD=debug_root_password
CONFIG_DB_DATABASE=nnks_db_debug
CONFIG_DB_PORT=3306
CONFIG_DB_SSL_MODE=DISABLED
CONFIG_DB_DEBUG_HOST=mysql-debug
CONFIG_DB_DEBUG_USER=root
CONFIG_DB_DEBUG_PASSWORD=debug_root_password
CONFIG_DB_DEBUG_DATABASE=nnks_db_debug
CONFIG_DB_DEBUG_PORT=3306
CONFIG_DB_DEBUG_SSL_MODE=DISABLED
REDIS_CONFIG_HOST=redis
REDIS_CONFIG_USER=default
REDIS_CONFIG_DATABASE=0
REDIS_CONFIG_PASSWORD=
REDIS_CONFIG_PORT=6379
REDIS_CONFIG_DEBUG_HOST=redis
REDIS_CONFIG_DEBUG_USER=default
REDIS_CONFIG_DEBUG_DATABASE=0
REDIS_CONFIG_DEBUG_PASSWORD=
REDIS_CONFIG_DEBUG_PORT=6379
ECONOMIC_API_APP_ACCESS_GRANT=ci-test
ECONOMIC_API_APP_ACCESS_GRANT2=ci-test-secondary
ECONOMIC_API_APP_SECRET_TOKEN=ci-test-secret
WORDPRESS_STATIC_TOKEN=ci-test
EMAIL_WASH_CERTIFICATE_TOKEN=ci-test
WORDPRESS_API_URL=http://localhost
MINIO_ENDPOINT=
MINIO_ACCESS_KEY=
MINIO_SECRET_KEY=
SLACK_DEFAULT_WEBHOOK=
EDGE_BROKER_URL=http://edge-broker:4300
EDGE_PUBLIC_BROKER_URL=http://localhost/api/edge-broker
EDGE_AUTH_MODE=manager
EDGE_BROKER_SHARED_SECRET=truckwash-edge-ci
EDGE_GATEWAY_VIEW_CACHE_TTL=0
TRUCKWASH_TEST_BLOCK_REAL_SHELLY=1
+90
View File
@@ -0,0 +1,90 @@
services:
traefik:
container_name: "${COMPOSE_PROJECT_NAME:-api}-traefik"
redis:
container_name: "${COMPOSE_PROJECT_NAME:-api}-redis"
mysql-debug:
container_name: "${COMPOSE_PROJECT_NAME:-api}-mysql-debug"
ports: !reset []
edge-broker:
container_name: "${COMPOSE_PROJECT_NAME:-api}-edge-broker"
ports:
- "127.0.0.1:${EDGE_BROKER_CI_PORT:-14300}:4300"
labels:
- "traefik.http.routers.edge-broker-local-ci.rule=PathPrefix(`/api/edge-broker`)"
- "traefik.http.routers.edge-broker-local-ci.entrypoints=web"
- "traefik.http.routers.edge-broker-local-ci.middlewares=secure-headers@file,edge-broker-strip-local"
- "traefik.http.routers.edge-broker-local-ci.priority=190"
- "traefik.http.routers.edge-broker-local-ci.service=edge-broker"
caddy:
container_name: "${COMPOSE_PROJECT_NAME:-api}-caddy"
depends_on: !reset []
labels:
- "traefik.http.routers.local-api-ci.rule=PathPrefix(`/api`)"
- "traefik.http.routers.local-api-ci.entrypoints=web"
- "traefik.http.routers.local-api-ci.middlewares=strip-api-prefix@file,secure-headers@file"
- "traefik.http.routers.local-api-ci.priority=90"
- "traefik.http.routers.local-api-ci.service=caddy"
volumes:
- ci_php_app:/var/www/html
php1:
container_name: "${COMPOSE_PROJECT_NAME:-api}-php1"
depends_on: !reset []
environment:
AUTO_COMPOSER_INSTALL: "false"
USE_ENV: "true"
CONFIG_DB_TARGET: "debug"
CONFIG_DB_HOST: "mysql-debug"
CONFIG_DB_USER: "root"
CONFIG_DB_PASSWORD: "debug_root_password"
CONFIG_DB_DATABASE: "nnks_db_debug"
CONFIG_DB_PORT: "3306"
CONFIG_DB_DEBUG_HOST: "mysql-debug"
CONFIG_DB_DEBUG_USER: "root"
CONFIG_DB_DEBUG_PASSWORD: "debug_root_password"
CONFIG_DB_DEBUG_DATABASE: "nnks_db_debug"
CONFIG_DB_DEBUG_PORT: "3306"
REDIS_CONFIG_HOST: "redis"
REDIS_CONFIG_PORT: "6379"
REDIS_CONFIG_DATABASE: "0"
REDIS_CONFIG_DEBUG_HOST: "redis"
REDIS_CONFIG_DEBUG_PORT: "6379"
REDIS_CONFIG_DEBUG_DATABASE: "0"
TRUCKWASH_TEST_BLOCK_REAL_SHELLY: "1"
EDGE_GATEWAY_VIEW_CACHE_TTL: "0"
volumes:
- ci_php_app:/var/www/html
php2:
container_name: "${COMPOSE_PROJECT_NAME:-api}-php2"
volumes:
- ci_php_app:/var/www/html
php3:
container_name: "${COMPOSE_PROJECT_NAME:-api}-php3"
volumes:
- ci_php_app:/var/www/html
php4:
container_name: "${COMPOSE_PROJECT_NAME:-api}-php4"
volumes:
- ci_php_app:/var/www/html
php5:
container_name: "${COMPOSE_PROJECT_NAME:-api}-php5"
volumes:
- ci_php_app:/var/www/html
volumes:
ci_php_app:
networks:
default:
ipam:
config:
- subnet: "${CI_DOCKER_SUBNET:-10.240.0.0/24}"
@@ -0,0 +1,57 @@
{
"name": "Protect default branch",
"target": "branch",
"enforcement": "active",
"bypass_actors": [
{
"actor_id": 5,
"actor_type": "RepositoryRole",
"bypass_mode": "pull_request"
}
],
"conditions": {
"ref_name": {
"exclude": [],
"include": [
"~DEFAULT_BRANCH"
]
}
},
"rules": [
{
"type": "deletion"
},
{
"type": "non_fast_forward"
},
{
"type": "required_linear_history"
},
{
"type": "pull_request",
"parameters": {
"allowed_merge_methods": [
"squash"
],
"dismiss_stale_reviews_on_push": false,
"require_code_owner_review": false,
"require_last_push_approval": false,
"required_approving_review_count": 0,
"required_review_thread_resolution": true
}
},
{
"type": "required_status_checks",
"parameters": {
"do_not_enforce_on_create": false,
"required_status_checks": [
{
"context": "Required CI",
"integration_id": 15368
}
],
"strict_required_status_checks_policy": true
}
}
]
}
+59 -13
View File
@@ -1,28 +1,74 @@
name: Qodana
on:
workflow_dispatch:
pull_request:
branches:
- master
- beta
- canary
- internal
types:
- opened
- reopened
- synchronize
- ready_for_review
push:
branches: # Specify your branches here
- main # The 'main' branch
- 'releases/*' # The release branches
branches:
- master
- beta
- canary
- internal
concurrency:
group: qodana-${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: true
jobs:
qodana:
runs-on: ubuntu-latest
name: Qodana
if: >-
github.event_name != 'pull_request' ||
(
github.event.pull_request.draft == false &&
github.event.pull_request.head.repo.full_name == github.repository &&
github.event.pull_request.user.login != 'dependabot[bot]'
)
runs-on: ubuntu-24.04
timeout-minutes: 60
permissions:
contents: write
pull-requests: write
contents: read
checks: write
pull-requests: write
steps:
- uses: actions/checkout@v3
- name: Require Qodana Cloud token
env:
QODANA_TOKEN: ${{ secrets.QODANA_TOKEN }}
shell: bash
run: |
set -euo pipefail
if [[ -z "${QODANA_TOKEN}" ]]; then
echo "::error::QODANA_TOKEN is not configured for this repository."
exit 1
fi
- name: Check out the analyzed commit
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
ref: ${{ github.event.pull_request.head.sha }} # to check out the actual pull request commit, not the merge commit
fetch-depth: 0 # a full history is required for pull request analysis
- name: 'Qodana Scan'
uses: JetBrains/qodana-action@v2025.3
ref: ${{ github.event.pull_request.head.sha || github.sha }}
fetch-depth: 0
persist-credentials: false
- name: Run Qodana
uses: JetBrains/qodana-action@4861e015da555e86a72b862892aba6c2b93e6891 # v2026.1.3
with:
pr-mode: false
pr-mode: ${{ github.event_name == 'pull_request' }}
use-caches: true
cache-default-branch-only: true
use-annotations: true
post-pr-comment: true
github-token: ${{ github.token }}
push-fixes: none
upload-result: false
env:
QODANA_TOKEN: ${{ secrets.QODANA_TOKEN }}
QODANA_ENDPOINT: 'https://qodana.cloud'
+13 -2
View File
@@ -14,7 +14,7 @@ jobs:
issues: write
steps:
- name: Checkout repository
uses: actions/checkout@v3
uses: actions/checkout@v4
- name: Create GitHub Issue for Copilot
env:
@@ -22,7 +22,18 @@ jobs:
TASK: ${{ github.event.inputs.task }}
ACTOR: ${{ github.actor }}
run: |
BODY="$(cat <<EOF
Assigned to Copilot by @$ACTOR.
Task
$TASK
AI workflow notes
- Generated assistant metadata is synchronized from the canonical AI workflow bundle.
- Run `node scripts/sync-ai-workflow.mjs --check` if assistant metadata changed.
EOF
)"
gh issue create \
--title "Copilot Task: $TASK" \
--body "Assigned to Copilot by @$ACTOR. Description: $TASK" \
--body "$BODY" \
--label "copilot-task"
+175
View File
@@ -0,0 +1,175 @@
name: Deploy to Hetzner (staging)
on:
push:
branches: [master]
workflow_dispatch:
inputs:
reason:
description: 'Reason for manual deploy'
required: false
default: 'manual'
concurrency:
group: deploy-${{ github.repository }}
cancel-in-progress: false
env:
DEPLOY_HOST: ${{ secrets.DEPLOY_HOST }}
DEPLOY_USER: ${{ secrets.DEPLOY_USER }}
jobs:
test-and-deploy:
name: CI + Deploy
runs-on: ubuntu-latest
timeout-minutes: 20
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 1
- name: Show commit info
run: |
echo "Repo: ${{ github.repository }}"
echo "Branch: ${{ github.ref }}"
echo "Commit: ${{ github.sha }}"
echo "Actor: ${{ github.actor }}"
# === CI (phpunit / vitest) runs here via repo's existing CI config ===
# (Most of our repos already have a "Required CI" check; this section
# would invoke that. If your repo doesn't have a CI workflow, the
# required-check on the branch will block this workflow's deploy step.)
- name: Setup SSH
uses: webfactory/ssh-agent@v0.9.0
with:
ssh-private-key: ${{ secrets.DEPLOY_SSH_KEY }}
- name: Add host key
run: |
mkdir -p ~/.ssh
ssh-keyscan -H "$DEPLOY_HOST" >> ~/.ssh/known_hosts 2>/dev/null
- name: Pre-deploy snapshot
id: pre
run: |
ssh "$DEPLOY_USER@$DEPLOY_HOST" '
set -e
cd /opt/${{ github.event.repository.name }}
git rev-parse HEAD > /tmp/last_deploy_sha
echo "PRE_SHA=$(cat /tmp/last_deploy_sha)"
echo "pre_sha=$(cat /tmp/last_deploy_sha)" >> $GITHUB_OUTPUT
'
- name: Deploy
id: deploy
run: |
ssh "$DEPLOY_USER@$DEPLOY_HOST" '
set -e
cd /opt/${{ github.event.repository.name }}
git fetch origin master
git reset --hard origin/master
# PHP repos: composer install + clear cache
if [ -f composer.json ]; then
composer install --no-dev --optimize-autoloader --no-interaction
php artisan cache:clear || true
php artisan config:cache || true
# Restart php-fpm if used
sudo systemctl reload php8.2-fpm || true
fi
# Node repos: npm ci + build
if [ -f package.json ]; then
npm ci --ignore-scripts
npm run build
# Restart node service
sudo systemctl reload pleno-vue || sudo systemctl reload nginx || true
fi
# Restart generic services
sudo systemctl reload nginx || true
# Install and start the cron-worker systemd service (long-running scheduler)
if [ -f services/nginx/app/resources/cron-worker.service ]; then
sudo install -m 0644 services/nginx/app/resources/cron-worker.service /etc/systemd/system/cron-worker.service
sudo systemctl daemon-reload
sudo systemctl enable cron-worker || true
sudo systemctl restart cron-worker || true
echo "cron-worker status: $(sudo systemctl is-active cron-worker || echo unknown)"
fi
echo "Deploy complete: $(git rev-parse --short HEAD)"
'
- name: Pre-deploy schema check (run all *_schema_bootstrap)
id: pre_schema
run: |
echo "Running schema bootstraps against the live database…"
# Idempotent — adds missing columns, never drops anything.
# Catches the "Unknown column 'invoice_email' in 'SELECT'"
# production failure mode (TRU-77) where migrations were
# merged to master but never applied to the live DB.
php scripts/run-schema-bootstraps.php
echo "Schema bootstraps complete."
- name: Alert Slack if schema-check fails (pre-deploy)
if: failure()
run: |
php scripts/schema-health-check.php > /tmp/schema.json 2>&1 || true
msg=$(jq -r '"Schema health FAILED on '$SMOKE_BASE_URL'\nMissing: " + (.missing | join(", "))' /tmp/schema.json 2>/dev/null || echo "Schema check produced no JSON")
curl -sS -X POST -H "Authorization: Bearer $SLACK_BOT_TOKEN" \
-H "Content-Type: application/json; charset=utf-8" \
https://slack.com/api/chat.postMessage \
-d "{\"channel\":\"$AI_DAILY_CHANNEL\",\"text\":\":rotating_light: *${{ github.event.repository.name }} — schema health FAIL\n${msg}\"}"
- name: Smoke test
id: smoke
continue-on-error: true
run: |
chmod +x scripts/smoke-test.sh
./scripts/smoke-test.sh
# Also hit the new admin schema-check endpoint to verify
# no required columns are missing.
echo "::group::Schema health check"
php scripts/schema-health-check.php | tee /tmp/schema-report.json
if [ "$(jq -r .ok /tmp/schema-report.json)" != "true" ]; then
echo "::error::Schema health check FAILED — missing columns:"
jq -r '.missing[]' /tmp/schema-report.json | sed 's/^/ • /'
exit 1
fi
echo "Schema health check OK."
- name: Auto-rollback on smoke failure
if: steps.smoke.outcome == 'failure'
run: |
echo "::error::Smoke test failed — rolling back to ${{ steps.pre.outputs.pre_sha }}"
ssh "$DEPLOY_USER@$DEPLOY_HOST" '
set -e
cd /opt/${{ github.event.repository.name }}
git reset --hard ${{ steps.pre.outputs.pre_sha }}
if [ -f composer.json ]; then
composer install --no-dev --optimize-autoloader --no-interaction
sudo systemctl reload php8.2-fpm || true
fi
if [ -f package.json ]; then
npm ci --ignore-scripts
npm run build
sudo systemctl reload nginx || true
fi
'
- name: Post Slack status
if: always()
uses: slackapi/slack-github-action@v1.27.0
with:
channel-id: ${{ secrets.AI_DAILY_CHANNEL }}
payload: |
{
"text": "${{ job.status == 'success' && '✅' || '❌' }} Deploy *${{ github.repository }}@${{ github.sha[0:7] }}* — ${{ job.status }}\n${{ steps.smoke.outcome == 'failure' && '⚠️ Auto-rolled back' || '✓ Smoke test passed' }}"
}
env:
SLACK_BOT_TOKEN: ${{ secrets.SLACK_BOT_TOKEN }}
- name: Update Linear issue
if: success() && steps.deploy.outcome == 'success'
run: |
# Find Linear issues in this commit's history and post a comment
# (uses GitHub's auto-link: if PR body contains "TRU-123" it auto-links)
# We skip this here; the OpenClaw cron `f26dfd83` handles Linear updates.
echo "Deploy notification will be picked up by OpenClaw cron."
+127
View File
@@ -0,0 +1,127 @@
name: Verify e-conomic Live
# Live verification of e-conomic export sanitization.
# Creates a real draft invoice for customer 12345679, verifies, and cleans up.
# Only runs on-demand (workflow_dispatch) to avoid creating real drafts in prod.
on:
workflow_dispatch:
inputs:
customer_number:
description: 'e-conomic customer number to test against'
required: false
default: '12345679'
type: string
dry_run:
description: 'Dry run (skip actual API calls, just verify env)'
required: false
default: 'true'
type: choice
options:
- 'true'
- 'false'
schedule:
# Run every Monday at 06:00 UTC to catch any drift in e-conomic behavior
- cron: '0 6 * * 1'
concurrency:
group: live-verify-economic
cancel-in-progress: false
permissions:
contents: read
jobs:
verify:
name: Live verify e-conomic draft flow
runs-on: ubuntu-24.04
timeout-minutes: 10
env:
ECONOMIC_API_APP_ACCESS_GRANT: ${{ secrets.ECONOMIC_API_APP_ACCESS_GRANT }}
ECONOMIC_API_APP_SECRET_TOKEN: ${{ secrets.ECONOMIC_API_APP_SECRET_TOKEN }}
ECONOMIC_API_BASE_URL: ${{ secrets.ECONOMIC_API_BASE_URL || 'https://restapi.e-conomic.com' }}
ECONOMIC_CUSTOMER_NUMBER: ${{ github.event.inputs.customer_number || '12345679' }}
steps:
- name: Checkout
uses: actions/checkout@11d5960a326750d5838078e36cf5b85af677262 # v4
with:
persist-credentials: false
- name: Setup PHP
uses: shivammathur/setup-php@e4a38cfe05f3813d096c1c2c0e7bf21a3100c93a # v2
with:
php-version: '8.4'
extensions: curl
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Dry-run mode (verify env only)
if: ${{ github.event.inputs.dry_run == 'true' }}
run: |
set -euo pipefail
echo "Dry-run mode: checking environment..."
if [ -z "${ECONOMIC_API_APP_ACCESS_GRANT:-}" ]; then
echo "::error::ECONOMIC_API_APP_ACCESS_GRANT is not set"
exit 1
fi
if [ -z "${ECONOMIC_API_APP_SECRET_TOKEN:-}" ]; then
echo "::error::ECONOMIC_API_APP_SECRET_TOKEN is not set"
exit 1
fi
# Mask secrets in logs
echo "ECONOMIC_API_APP_ACCESS_GRANT=${ECONOMIC_API_APP_ACCESS_GRANT:0:8}..."
echo "ECONOMIC_API_APP_SECRET_TOKEN=${ECONOMIC_API_APP_SECRET_TOKEN:0:4}..."
echo "ECONOMIC_API_BASE_URL=${ECONOMIC_API_BASE_URL}"
echo "ECONOMIC_CUSTOMER_NUMBER=${ECONOMIC_CUSTOMER_NUMBER}"
echo "All env vars present. Re-run with dry_run=false to do a live test."
- name: Run live verification (creates and cleans up a real draft)
if: ${{ github.event.inputs.dry_run == 'false' }}
run: |
set -euo pipefail
cd /workspace/copenhagentruckwash/api
# Use the script that's checked in
# (we expect the script to be in the repo, e.g., scripts/verify-economic-drafts-live.php)
if [ -f scripts/verify-economic-drafts-live.php ]; then
php8.4 scripts/verify-economic-drafts-live.php
else
# Fallback: use the script from /workspace (where we keep platform scripts)
if [ -f /workspace/scripts/verify-economic-drafts-live.php ]; then
php8.4 /workspace/scripts/verify-economic-drafts-live.php
else
echo "::error::Live verification script not found"
exit 1
fi
fi
- name: Upload verification logs
if: ${{ always() }}
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
with:
name: live-verify-logs
path: |
/tmp/verify-economic-*.log
.tmp/verify-economic-*.log
if-no-files-found: warn
retention-days: 7
- name: Notify Slack on failure
if: ${{ failure() && env.SLACK_BOT_TOKEN != '' }}
env:
SLACK_BOT_TOKEN: ${{ secrets.SLACK_BOT_TOKEN }}
SLACK_DEFAULT_WEBHOOK: ${{ secrets.SLACK_DEFAULT_WEBHOOK }}
AI_DAILY_CHANNEL: ${{ secrets.AI_DAILY_CHANNEL || 'C0AM3E43249' }}
run: |
set -euo pipefail
if [ -n "${SLACK_DEFAULT_WEBHOOK:-}" ]; then
curl -fsS -X POST "$SLACK_DEFAULT_WEBHOOK" \
-H 'Content-Type: application/json' \
-d "$(cat <<EOF
{
"channel": "$AI_DAILY_CHANNEL",
"text": ":rotating_light: e-conomic live verification failed\nWorkflow: ${{ github.workflow }}\nRun: ${{ github.run_id }}\nURL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}"
}
EOF
)"
fi
+393 -67
View File
@@ -4,89 +4,415 @@ on:
pull_request:
push:
branches:
- main
- master
- beta
- canary
- internal
permissions:
contents: read
concurrency:
group: ${{ github.workflow }}-${{ github.event_name == 'pull_request' && github.event.pull_request.number || github.run_id }}
cancel-in-progress: ${{ github.event_name == 'pull_request' }}
jobs:
unit:
name: Unit (required)
runs-on: ubuntu-latest
php:
name: PHP ${{ matrix.suite }} (required)
# Docker jobs use disposable workspaces so root-owned container artifacts cannot poison later checkouts.
runs-on: ubuntu-24.04
strategy:
fail-fast: false
matrix:
suite: [unit, integration, api, legacy]
env:
DOCKER_HOST: unix:///var/run/docker.sock
COMPOSE_PROJECT_NAME: php-${{ github.run_id }}-${{ github.job }}-${{ matrix.suite }}-${{ github.run_attempt }}
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Setup PHP
uses: shivammathur/setup-php@v2
uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4
with:
php-version: '8.2'
extensions: mysqli, curl, openssl, json, redis, pcov
coverage: pcov
persist-credentials: false
- name: Resolve dependencies
working-directory: services/nginx/app
run: composer update --no-interaction --prefer-dist
- name: Ensure Docker access
run: |
set -euo pipefail
docker ps >/dev/null 2>&1 || {
echo "Docker is unavailable to the runner identity. Fix the isolated runner configuration; the workflow will not weaken /var/run/docker.sock permissions." >&2
exit 1
}
- name: Run unit tests
working-directory: services/nginx/app
run: composer test:unit
- name: Generate coverage report
working-directory: services/nginx/app
run: composer test:coverage
- name: Upload coverage artifact
uses: actions/upload-artifact@v4
- name: Setup Node.js
if: ${{ matrix.suite == 'unit' }}
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4
with:
name: unit-coverage-clover
path: services/nginx/app/build/logs/clover.xml
node-version: 22
- name: Check AI workflow sync
if: ${{ matrix.suite == 'unit' }}
run: node scripts/sync-ai-workflow.mjs --check
- name: Run PHP ${{ matrix.suite }} suite
run: bash scripts/php-ci-test.sh ${{ matrix.suite }}
- name: Upload PHP suite logs
if: ${{ failure() }}
continue-on-error: true
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
with:
name: php-${{ matrix.suite }}-logs
path: .tmp/ci-logs/${{ matrix.suite }}
if-no-files-found: warn
retention-days: 3
integration:
name: Integration (advisory)
runs-on: ubuntu-latest
continue-on-error: true
services:
redis:
image: redis:7
ports:
- 6379:6379
mysql:
image: mysql:8
env:
MYSQL_DATABASE: app_test
MYSQL_ROOT_PASSWORD: root
ports:
- 3306:3306
options: >-
--health-cmd="mysqladmin ping -h 127.0.0.1 -proot"
--health-interval=10s
--health-timeout=5s
--health-retries=10
edge-agent:
name: Edge Agent (required)
runs-on: ubuntu-24.04
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Setup PHP
uses: shivammathur/setup-php@v2
uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4
with:
php-version: '8.2'
extensions: mysqli, curl, openssl, json, redis
persist-credentials: false
- name: Setup Node.js
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4
with:
node-version: 22
- name: Install native build tools
run: |
set -euo pipefail
if command -v make >/dev/null 2>&1 && command -v g++ >/dev/null 2>&1; then
exit 0
fi
if ! command -v apt-get >/dev/null 2>&1; then
echo "make and g++ are required to install node-pty, but apt-get is not available on this runner." >&2
exit 1
fi
apt_cmd=(apt-get)
if [ "$(id -u)" -ne 0 ]; then
if ! command -v sudo >/dev/null 2>&1; then
echo "make and g++ are missing, and sudo is not available to install them." >&2
exit 1
fi
apt_cmd=(sudo apt-get)
fi
"${apt_cmd[@]}" update
"${apt_cmd[@]}" install -y --no-install-recommends build-essential python3
- name: Install dependencies
working-directory: services/edge-agent
run: npm ci
- name: Run edge agent tests
working-directory: services/edge-agent
run: npm test
edge-broker:
name: Edge Broker (required)
runs-on: ubuntu-24.04
env:
DOCKER_HOST: unix:///var/run/docker.sock
steps:
- name: Checkout
uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4
with:
persist-credentials: false
- name: Ensure Docker access
run: |
set -euo pipefail
docker ps >/dev/null 2>&1 || {
echo "Docker is unavailable to the runner identity. Fix the isolated runner configuration; the workflow will not weaken /var/run/docker.sock permissions." >&2
exit 1
}
- name: Materialize CI compose env files
run: |
set -euo pipefail
cp .github/ci.env .env
cp .github/ci.env.staging .env.staging
- name: Validate compose contracts
run: |
docker compose -f docker-compose.yml -f docker-compose.prod.yml config > /dev/null
docker compose -f docker-compose.example.yml config > /dev/null
- name: Setup Node.js
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4
with:
node-version: 22
- name: Install dependencies
working-directory: services/edge-broker
run: npm ci
- name: Run edge broker tests
working-directory: services/edge-broker
run: npm test
edge-gateway-backend:
name: Edge Gateway Backend (required)
runs-on: ubuntu-24.04
env:
DOCKER_HOST: unix:///var/run/docker.sock
COMPOSE_FILE: docker-compose.yml:.github/docker-compose.ci.yml
COMPOSE_PROJECT_NAME: edge-gateway-backend-${{ github.run_id }}-${{ github.run_attempt }}
COMPOSE_PROFILES: dev
TRAEFIK_WEB_PORT: "18080"
TRAEFIK_WEBSECURE_PORT: "18443"
TRAEFIK_WEBSECURE_STAGING_PORT: "18433"
TRAEFIK_METRICS_PORT: "19100"
EDGE_BROKER_CI_PORT: "14300"
EDGE_GATEWAY_E2E_BASE_URL: "http://localhost:18080/api"
steps:
- name: Checkout
uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4
with:
persist-credentials: false
- name: Ensure Docker access
run: |
set -euo pipefail
docker ps >/dev/null 2>&1 || {
echo "Docker is unavailable to the runner identity. Fix the isolated runner configuration; the workflow will not weaken /var/run/docker.sock permissions." >&2
exit 1
}
- name: Allocate CI ports
run: |
set -euo pipefail
find_free_port() {
start="$1"
end="$2"
port="$start"
while [ "$port" -le "$end" ]; do
if ! ss -H -ltn "sport = :$port" 2>/dev/null | grep -q .; then
echo "$port"
return 0
fi
port=$((port + 1))
done
echo "No free port in range ${start}-${end}." >&2
exit 1
}
base=$((20000 + (GITHUB_RUN_ID % 20000)))
web_port="$(find_free_port "$base" "$((base + 2000))")"
websecure_port="$(find_free_port "$((web_port + 1))" "$((web_port + 2000))")"
staging_port="$(find_free_port "$((websecure_port + 1))" "$((websecure_port + 2000))")"
metrics_port="$(find_free_port "$((staging_port + 1))" "$((staging_port + 2000))")"
broker_port="$(find_free_port "$((metrics_port + 1))" "$((metrics_port + 2000))")"
checksum="$(printf '%s' "$COMPOSE_PROJECT_NAME" | cksum | awk '{print $1}')"
subnet_second=$((64 + ((checksum / 256) % 64)))
subnet_third=$((checksum % 256))
ci_docker_subnet="10.${subnet_second}.${subnet_third}.0/24"
{
echo "TRAEFIK_WEB_PORT=${web_port}"
echo "TRAEFIK_WEBSECURE_PORT=${websecure_port}"
echo "TRAEFIK_WEBSECURE_STAGING_PORT=${staging_port}"
echo "TRAEFIK_METRICS_PORT=${metrics_port}"
echo "EDGE_BROKER_CI_PORT=${broker_port}"
echo "CI_DOCKER_SUBNET=${ci_docker_subnet}"
echo "EDGE_GATEWAY_E2E_BASE_URL=http://localhost:${web_port}/api"
echo "EDGE_GATEWAY_E2E_COMPOSE_PROJECT=${COMPOSE_PROJECT_NAME}"
} >> "$GITHUB_ENV"
- name: Materialize CI compose env files
run: |
set -euo pipefail
cp .github/ci.env .env
cp .github/ci.env.staging .env.staging
printf '\nEDGE_PUBLIC_BROKER_URL=http://edge-broker:4300/edge-broker\n' >> .env
- name: Setup Node.js
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4
with:
node-version: 22
- name: Boot local stack
run: sh scripts/ci-docker-compose-up.sh traefik redis mysql-debug edge-broker php1 php2 php3 php4 php5 caddy
- name: Sync PHP app checkout
run: >
tar
--exclude='./vendor'
--exclude='./.phpunit.cache'
--exclude='./build/logs'
-C services/nginx/app -cf - .
| docker compose -f docker-compose.yml -f .github/docker-compose.ci.yml exec -T php1 tar --no-same-owner -C /var/www/html -xf -
- name: Resolve dependencies
working-directory: services/nginx/app
run: composer update --no-interaction --prefer-dist
run: |
set -euo pipefail
composer_install() {
install_mode="$1"
max_attempts="$2"
attempt=1
while :; do
if docker compose -f docker-compose.yml -f .github/docker-compose.ci.yml exec -T php1 sh -lc "cd /var/www/html && composer install --no-interaction ${install_mode} --no-progress"; then
return 0
fi
if [ "$attempt" -ge "$max_attempts" ]; then
return 1
fi
sleep_seconds=$((attempt * 5))
echo "composer install ${install_mode} failed; retrying in ${sleep_seconds}s (attempt $((attempt + 1))/${max_attempts})" >&2
sleep "$sleep_seconds"
attempt=$((attempt + 1))
done
}
- name: Run integration tests
working-directory: services/nginx/app
composer_install --prefer-dist 3 || {
echo "Composer dist install failed; retrying with --prefer-source." >&2
composer_install --prefer-source 2
}
- name: Verify edge gateway test files
run: >
docker compose -f docker-compose.yml -f .github/docker-compose.ci.yml exec -T php1 sh -lc
"cd /var/www/html &&
php -r '\$composer = json_decode(file_get_contents(\"composer.json\"), true); echo \"Composer scripts: \", implode(\",\", array_keys(\$composer[\"scripts\"] ?? [])), PHP_EOL;' &&
find tests/Api -maxdepth 1 -type f -name 'EdgeGateway*ApiTest.php' -print &&
test -f tests/Api/EdgeGatewayAgentApiTest.php &&
test -f tests/Api/EdgeGatewayBrokerApiTest.php &&
test -f tests/Api/EdgeGatewayOperatorApiTest.php"
- name: Run edge gateway API tests
run: >
docker compose -f docker-compose.yml -f .github/docker-compose.ci.yml exec -T php1 sh -lc
"cd /var/www/html &&
RUN_API_TESTS=1
API_TEST_BOOTSTRAP_SCHEMA=1
API_TEST_ALLOW_LIVE_DB=1
CONFIG_DB_TARGET=debug
CONFIG_DB_HOST=mysql-debug
CONFIG_DB_USER=\${CONFIG_DB_USER:-root}
CONFIG_DB_PASSWORD=\${CONFIG_DB_PASSWORD:-debug_root_password}
CONFIG_DB_DATABASE=\${CONFIG_DB_DATABASE:-nnks_db_debug}
CONFIG_DB_PORT=3306
CONFIG_DB_DEBUG_HOST=mysql-debug
CONFIG_DB_DEBUG_USER=\${CONFIG_DB_DEBUG_USER:-root}
CONFIG_DB_DEBUG_PASSWORD=\${CONFIG_DB_DEBUG_PASSWORD:-debug_root_password}
CONFIG_DB_DEBUG_DATABASE=\${CONFIG_DB_DEBUG_DATABASE:-nnks_db_debug}
CONFIG_DB_DEBUG_PORT=3306
API_TEST_REQUEST_TIMEOUT=180
EDGE_GATEWAY_VIEW_CACHE_TTL=0
EDGE_BROKER_URL=
vendor/bin/pest
tests/Api/EdgeGatewayAgentApiTest.php
tests/Api/EdgeGatewayBrokerApiTest.php
tests/Api/EdgeGatewayOperatorApiTest.php
--colors=always"
- name: Run edge gateway integration tests
run: >
docker compose -f docker-compose.yml -f .github/docker-compose.ci.yml exec -T php1 sh -lc
"cd /var/www/html &&
RUN_INTEGRATION_TESTS=1
CONFIG_DB_TARGET=debug
CONFIG_DB_HOST=mysql-debug
CONFIG_DB_USER=\${CONFIG_DB_USER:-root}
CONFIG_DB_PASSWORD=\${CONFIG_DB_PASSWORD:-debug_root_password}
CONFIG_DB_DATABASE=\${CONFIG_DB_DATABASE:-nnks_db_debug}
CONFIG_DB_PORT=3306
CONFIG_DB_DEBUG_HOST=mysql-debug
CONFIG_DB_DEBUG_USER=\${CONFIG_DB_DEBUG_USER:-root}
CONFIG_DB_DEBUG_PASSWORD=\${CONFIG_DB_DEBUG_PASSWORD:-debug_root_password}
CONFIG_DB_DEBUG_DATABASE=\${CONFIG_DB_DEBUG_DATABASE:-nnks_db_debug}
CONFIG_DB_DEBUG_PORT=3306
EDGE_BROKER_URL=
vendor/bin/pest tests/Integration/EdgeGateway --colors=always"
- name: Run edge gateway E2E smoke
env:
RUN_INTEGRATION_TESTS: '1'
REDIS_CONFIG_HOST: 127.0.0.1
REDIS_CONFIG_DATABASE: '0'
REDIS_CONFIG_PASSWORD: ''
CONFIG_DB_HOST: 127.0.0.1
CONFIG_DB_USER: root
CONFIG_DB_PASSWORD: root
CONFIG_DB_DATABASE: app_test
run: composer test:integration
EDGE_GATEWAY_E2E_COPY_CONFIG: "true"
EDGE_GATEWAY_E2E_SKIP_COMPOSE_UP: "true"
run: node scripts/edge-gateway-e2e.mjs
- name: Tear down local stack
if: always()
run: docker compose -f docker-compose.yml -f .github/docker-compose.ci.yml down -v
required-ci:
name: Required CI
runs-on: ubuntu-latest
needs: [php, edge-agent, edge-broker, edge-gateway-backend]
if: ${{ always() }}
steps:
- name: Verify required jobs succeeded
env:
PHP_RESULT: ${{ needs.php.result }}
EDGE_AGENT_RESULT: ${{ needs.edge-agent.result }}
EDGE_BROKER_RESULT: ${{ needs.edge-broker.result }}
EDGE_GATEWAY_BACKEND_RESULT: ${{ needs.edge-gateway-backend.result }}
run: |
set -euo pipefail
failed=0
for dependency in \
"php=${PHP_RESULT}" \
"edge-agent=${EDGE_AGENT_RESULT}" \
"edge-broker=${EDGE_BROKER_RESULT}" \
"edge-gateway-backend=${EDGE_GATEWAY_BACKEND_RESULT}"
do
name="${dependency%%=*}"
result="${dependency#*=}"
if [ "$result" != "success" ]; then
echo "Required dependency ${name} completed with result: ${result:-missing}" >&2
failed=1
fi
done
test "$failed" -eq 0
release-manager-gate:
name: Release Manager gate
runs-on: ubuntu-24.04
needs: [required-ci]
if: ${{ github.event_name == 'push' && github.ref == 'refs/heads/master' && needs.required-ci.result == 'success' }}
steps:
- name: Record Release Manager API gate
run: |
set -euo pipefail
test -n "$RELEASE_MANAGER_GATE_TOKEN" || (echo "RELEASE_MANAGER_GATE_TOKEN is required" >&2; exit 1)
response_file="$(mktemp)"
http_code="$(curl --show-error --silent \
--connect-timeout 10 \
--retry 5 \
--retry-all-errors \
--retry-delay 15 \
--retry-max-time 300 \
-o "$response_file" \
-w '%{http_code}' \
-X POST "$RELEASE_MANAGER_GATE_URL" \
-H "Authorization: Bearer $RELEASE_MANAGER_GATE_TOKEN" \
-H "Content-Type: application/json" \
--data "{\"channel_slug\":\"stable\",\"app\":\"api\",\"repository\":\"$RELEASE_REPOSITORY\",\"branch\":\"$RELEASE_BRANCH\",\"expected_commit\":\"$RELEASE_EXPECTED_COMMIT\",\"workflow_url\":\"$RELEASE_WORKFLOW_URL\",\"auto_sync\":true,\"wait_timeout_seconds\":300,\"poll_interval_seconds\":10,\"required_checks\":[\"api_gateway\"]}")"
response_body="$(cat "$response_file")"
rm -f "$response_file"
if [[ "$http_code" =~ ^2[0-9][0-9]$ ]]; then
printf '%s\n' "$response_body"
exit 0
fi
printf '%s\n' "$response_body"
echo "Release Manager gate failed with HTTP $http_code." >&2
exit 1
env:
RELEASE_MANAGER_GATE_URL: ${{ secrets.RELEASE_MANAGER_GATE_URL || 'https://api.truckwash.io/release/gate/test-runs' }}
RELEASE_MANAGER_GATE_TOKEN: ${{ secrets.RELEASE_MANAGER_GATE_TOKEN }}
RELEASE_REPOSITORY: ${{ github.repository }}
RELEASE_BRANCH: ${{ github.ref_name }}
RELEASE_EXPECTED_COMMIT: ${{ github.sha }}
RELEASE_WORKFLOW_URL: https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}
+9 -1
View File
@@ -2,6 +2,7 @@
/docker-compose.yml
/services/nginx/app/vendor/
/services/nginx/app/modules/washcertificates/vendor/
/services/nginx/app/.phpunit.cache/
/services/nginx/letsencrypt/
*.pem
*.log.gz
@@ -9,4 +10,11 @@
/services/php/logs/
/.idea/
.env
/services/caddy/logs*
/services/caddy/logs*
.env.old
/.tmp/
/.env.staging
/services/nginx/app/storage/replication-bootstrap.json
/.env_old_2
/.openclaw/
/services/nginx/app/build/phpstan/
+19 -139
View File
@@ -1,148 +1,28 @@
### Copenhagen Truck Wash API — Development Guidelines (Projectspecific)
<!-- AUTOGENERATED: Run `node scripts/sync-ai-workflow.mjs --write`. -->
#### Scope
This document captures projectspecific knowledge for building, configuring, testing, and extending the API. It assumes an advanced developer familiar with Docker, PHP 8.2, and HTTP APIs.
# Copenhagen Truck Wash API Development Guidelines
---
This file is generated from the canonical AI workflow and is the supported Junie-facing reference for the backend repository.
### Build and Configuration
## Build And Run
- Stack overview (local):
- Reverse proxy/router: Traefik 2.x (`docker-compose.yml` service `traefik`).
- Web server: Caddy (`caddy`) serving the PHP app from `services/nginx/app` and proxied by Traefik.
- PHP runtime: Multiple PHPFPM containers (`php1`..`php5`), sharing the bindmounted app directory.
- Redis: `redis` for caching/queues/locks.
- Project root: `services/nginx/app` is the effective PHP application root.
- Setup: use `./scripts/setup.sh` on POSIX or `powershell -ExecutionPolicy Bypass -File .\scripts\setup.ps1` on Windows.
- Start local API stack: `docker compose up -d traefik redis mysql-debug php1 caddy`.
- Tail logs with `docker compose logs -f --tail=200 php1`.
- App location: `services/nginx/app` is the effective application root (many scripts/tests derive `WD` to point here).
## Testing
- Composer and dependencies:
- `services/php/Dockerfile` installs Composer and PHP extensions.
- `services/php/docker-entrypoint.sh` performs a guarded Composer install on `php1` at container start when `AUTO_COMPOSER_INSTALL=true` and `composer.json` is present.
- App dependencies live under `services/nginx/app/composer.json` (note: very light, primarily runtime libs; dev tool `rector/rector`).
- Supported backend validation runs in `php1`.
- Unit tests: `docker compose exec -T php1 sh -lc "cd /var/www/html && composer test:unit"`.
- Integration tests: `docker compose exec -T php1 sh -lc "cd /var/www/html && composer test:integration"`.
- API tests: `docker compose exec -T php1 sh -lc "cd /var/www/html && composer test:api"`.
- Prefer the narrowest suite that proves the change, then run the broader suite that matches the risk.
- Configuration source of truth during containerized runs is environment variables consumed by `services/nginx/app/config.php`.
- `config.php` requires `USE_ENV=true`; otherwise it throws an exception. Many CLI scripts/tests bypass `config.php` entirely to remain envagnostic.
## Workflow Notes
- Local run targets and routing:
- Traefik exposes:
- `http://localhost` → routes to Caddy → app (HTTP only for dev).
- `https://localhost` → also mapped, using Traefiks default/selfsigned dev cert.
- `http(s)://localhost/api/*` → Traefik stripprefix middleware forwards to Caddy; the app sees paths without the `/api` prefix.
- For productionlike HTTPS with real certs, see `README.md` for `LETSENCRYPT_PATH` mounting strategy (only needed if you want the exact `api.truckwash.dk` TLS behavior locally).
- Generated assistant metadata is checked with `node scripts/sync-ai-workflow.mjs --check`.
- Route and schema changes require matching updates to `openapi.yaml`.
- Runtime OpenAI product behavior is out of scope for this workflow bundle unless a task explicitly changes product code.
- Minimal bringup for local development:
- Prerequisites: Docker Desktop 4.x+.
- First run will build PHP images and start dependent services. Composer install runs automatically on `php1`.
- Recommended minimal set:
- `docker compose up -d traefik redis php1 caddy`
- Full set (scale out PHP or add observability as needed):
- `docker compose up -d` (starts `traefik`, `redis`, `caddy`, `php1`..`php5`, and other declared services).
- Logs:
- `docker compose logs -f caddy`
- `docker compose logs -f php1`
- `docker compose logs -f traefik`
- Security note: `docker-compose.yml` currently embeds sensitive env values (DB, API tokens). Treat the file as secret in private repos; never republish as is. Prefer `.env` overrides and secrets providers for wider teams.
---
### Testing
The repository does not use PHPUnit for the app. Instead, tests are lightweight CLI scripts under `services/nginx/app/tests`. Conventions:
- Test style
- Selfcontained procedural PHP scripts intended to be executed with `php`. No framework required.
- Many tests define the `WD` constant to the app root and then `require_once` specific class/trait/interface files they exercise.
- Integrationstyle scripts that need configuration will rely on `services/nginx/app/config.php` and so must run within a properly provisioned environment (Docker containers with `USE_ENV=true`).
- Fast unitstyle scripts should avoid `config.php` and any I/O; they manually include only whats needed and/or use test doubles.
- Running tests from host (fastest path)
- Prereq: PHP CLI available on host. Verified with:
- `php -v` → observed on our env: `PHP 8.2.30 (cli)`
- Example: an existing, fully selfcontained test validating subuser permission wiring:
- Command:
- `php services/nginx/app/tests/subusers/SelfservePermissionInitTest.php`
- Verified output (captured):
-
```
✔ SELFSERVE_ADD is granted as expected
✔ SELFSERVE_LIST is not granted as expected
✔ SELFSERVE_EDIT is not granted as expected
✔ SELFSERVEDELETE is not granted as expected
SelfservePermissionInitTest completed.
```
- Running tests inside Docker (when host PHP is unavailable or when env is required)
- Ensure containers are up: `docker compose up -d traefik redis php1 caddy`
- Execute a test within `php1`:
- `docker compose exec -T php1 php /var/www/html/tests/subusers/SelfservePermissionInitTest.php`
- For integration tests that depend on `config.php` (e.g., Redis/DB), environment variables are preconfigured in `docker-compose.yml` for the PHP services. Running inside `php1` will satisfy `USE_ENV=true`.
- Adding a new test
- Place the file under `services/nginx/app/tests/<domain>/YourTest.php`.
- At top of the file, define `WD` if not already defined:
```php
if (!defined('WD')) { define('WD', dirname(__DIR__, 2)); }
```
- Prefer selfcontained tests that avoid I/O. If you need to touch internal classes without Composer autoloading, include files directly, mirroring existing tests.
- If the test must hit Redis/DB or rely on globals from `config.php`, run it inside a PHP container (`php1`) with `USE_ENV=true`.
- Demonstration: creating and running a minimal test
- We created a temporary, envagnostic test at `services/nginx/app/tests/examples/HelloWorldTest.php` with these semantics:
- Define `WD`, perform trivial assertions, and print success markers.
- Command executed and verified output:
- Command:
- `php services/nginx/app/tests/examples/HelloWorldTest.php`
- Output (captured):
-
```
✔ Basic arithmetic works (2 + 2 = 4)
✔ WD points to the application root: C:\\Users\\2jepp\\PhpstormProjects\\api\\services\\nginx\\app
HelloWorldTest completed.
```
- The example file was removed afterwards to keep the repository unchanged. You can replicate by creating a similar file and removing it after execution.
---
### Additional Development Information
- Routing and HTTP surface
- Routes live under `services/nginx/app/routes`. Example route `exampleRoute.php` exposes `GET /example` returning `{"message":"Hello World!"}`. Local access paths (with Traefik):
- `http://localhost/example` (direct)
- `http://localhost/api/example` (Traefik stripprefix `/api` → still routes to `/example` in the app).
- The OpenAPI contract is at the repo root `openapi.yaml` (large, authoritative). Keep it synchronized with implemented routes and payloads.
- Module layout and traits
- Domain modules live in `services/nginx/app/modules/*` and are heavily traitbased. Tests often pull in precise files from here to avoid full app bootstrap.
- Example: subusers module (`modules/subusers/...`) provides permission node containers and helpers. The test `tests/subusers/SelfservePermissionInitTest.php` demonstrates overriding DBbacked methods to inject permissions for fast, deterministic checks.
- Config and globals
- `services/nginx/app/config.php` populates globals like `$CONFIG_DB`, `$REDIS_CONFIG`, etc., but only when `USE_ENV=true`. If you see tests throwing “Environment variables are not set”, run them inside Docker or set required envs for host PHP.
- Code style
- `.editorconfig` at repo root configures formatting. Key PHP rules:
- Encoding: UTF8 with CRLF line endings.
- Indent: 4 spaces; continuation indent 4.
- Class brace style: next_line; function/method blank lines: 1.
- Import sorting: alphabetic; various alignment toggles are disabled.
- Match existing patterns (procedural scripts for tests; namespacing in app code; traits for crosscutting concerns). Avoid adding new frameworks for tests unless explicitly requested.
- Composer / Autoload
- There is no Composer autoload configured for the app code in `composer.json`; tests include files directly. If you introduce autoloading, coordinate with Docker entrypoint behaviors and ensure zerodowntime for existing scripts.
- Performance & reliability hints for tests
- Prefer small, deterministic CLI scripts; inject dependencies and override I/O methods (as shown in `SelfservePermissionInitTest.php`).
- Avoid hitting external services (Stripe, economic, MinIO) from tests; instead, stub/override or provide fakes.
---
### Quick Reference
- Bring up minimal dev stack:
- `docker compose up -d traefik redis php1 caddy`
- Run a fast, selfcontained test (host PHP):
- `php services/nginx/app/tests/subusers/SelfservePermissionInitTest.php`
- Run a test inside Docker (envbacked):
- `docker compose exec -T php1 php /var/www/html/tests/subusers/SelfservePermissionInitTest.php`
Canonical workflow reference: `.ai-workflow/workflow.md`.
Binary file not shown.
+4 -2
View File
@@ -40,13 +40,15 @@ COPY . /var/www/html
# Copy Nginx configuration file
COPY nginx.conf /etc/nginx/nginx.conf
COPY services/php/php-fpm-pool.conf /usr/local/etc/php-fpm.d/zz-pleno-workers.conf
# Install Composer
COPY --from=composer:2.6 /usr/bin/composer /usr/bin/composer
# Runtime bootstrap: lightweight entrypoint to ensure Composer deps exist when app is bind-mounted
COPY services/php/docker-entrypoint.sh /usr/local/bin/docker-entrypoint.sh
RUN chmod +x /usr/local/bin/docker-entrypoint.sh
RUN sed -i 's/\r$//' /usr/local/bin/docker-entrypoint.sh \
&& chmod +x /usr/local/bin/docker-entrypoint.sh
# Install PHP dependencies through Composer (only where composer.json exists)
# Main app dependencies
@@ -72,4 +74,4 @@ EXPOSE 80 443
ENTRYPOINT ["docker-entrypoint.sh"]
# Start services when no command is provided (docker-compose overrides this with ["php-fpm"])
CMD ["php-fpm"]
CMD ["php-fpm"]
+80
View File
@@ -0,0 +1,80 @@
FROM php:8.2.15-fpm
WORKDIR /var/www/html
COPY --from=composer:2.6 /usr/bin/composer /usr/bin/composer
RUN set -eux; \
apt-get update; \
apt-get install -y --no-install-recommends \
$PHPIZE_DEPS \
ca-certificates \
curl \
default-mysql-client \
git \
imagemagick \
libfreetype6-dev \
libjpeg62-turbo-dev \
libmagickcore-dev \
libmagickwand-dev \
libonig-dev \
libpng-dev \
libssl-dev \
libxml2-dev \
libzip-dev \
mariadb-client \
nginx \
openssl \
pkg-config \
redis-tools \
unzip \
zip; \
update-ca-certificates; \
docker-php-ext-configure gd --with-freetype --with-jpeg; \
docker-php-ext-install -j"$(nproc)" \
bcmath \
exif \
gd \
mbstring \
mysqli \
pcntl \
pdo_mysql \
sockets \
zip; \
pecl install imagick-3.7.0 redis; \
docker-php-ext-enable imagick redis; \
apt-get purge -y --auto-remove -o APT::AutoRemove::RecommendsImportant=false $PHPIZE_DEPS; \
rm -rf /var/lib/apt/lists/*
COPY services/nginx/app/ /var/www/html/
COPY scripts/bird-control-plane-activate.php /var/www/html/scripts/bird-control-plane-activate.php
COPY scripts/bird-control-plane-auto-activate.php /var/www/html/scripts/bird-control-plane-auto-activate.php
COPY services/php/docker-entrypoint.sh /usr/local/bin/docker-entrypoint.sh
COPY services/php/php-fpm-pool.conf /usr/local/etc/php-fpm.d/zz-pleno-workers.conf
COPY services/coolify/api/nginx.conf /etc/nginx/nginx.conf
COPY services/coolify/api/start.sh /usr/local/bin/coolify-api-start
RUN set -eux; \
rm -f /var/www/html/storage/replication-bootstrap.json /var/www/html/storage/replication-bootstrap-*.json; \
sed -i 's/\r$//' /usr/local/bin/docker-entrypoint.sh /usr/local/bin/coolify-api-start; \
chmod +x /usr/local/bin/docker-entrypoint.sh /usr/local/bin/coolify-api-start; \
COMPOSER_ALLOW_SUPERUSER=1 composer install --no-dev --prefer-dist --optimize-autoloader --no-interaction -d /var/www/html; \
if [ -f /var/www/html/modules/washcertificates/composer.json ]; then \
COMPOSER_ALLOW_SUPERUSER=1 composer install --no-dev --prefer-dist --optimize-autoloader --no-interaction -d /var/www/html/modules/washcertificates; \
fi; \
COMPOSER_ALLOW_SUPERUSER=1 composer dump-autoload --no-dev --optimize --no-interaction -d /var/www/html; \
php -d display_errors=1 -r 'require "/var/www/html/vendor/autoload.php"; exit(interface_exists("Psr\\Http\\Message\\UriInterface") && interface_exists("Psr\\Http\\Message\\StreamInterface") ? 0 : 1);'; \
php -r 'exit(function_exists("proc_open") && extension_loaded("openssl") ? 0 : 1);'; \
test "$(openssl pkey -pubin -in /var/www/html/modules/bird/resources/control-plane-bootstrap-public.pem -outform DER | sha256sum | cut -d " " -f 1)" = "6dc63c6ffe33b8de0b1396d7f529f56aea0a685ef98168016161cf721ddc8c21"; \
chown -R www-data:www-data /var/www/html; \
chmod -R 755 /var/www/html
ENV APP_DIR=/var/www/html \
MODULE_DIR=/var/www/html/modules/washcertificates \
AUTO_COMPOSER_INSTALL=false \
COMPOSER_ALLOW_SUPERUSER=1
EXPOSE 80
ENTRYPOINT ["/usr/local/bin/docker-entrypoint.sh"]
CMD ["coolify-api-start"]
+134
View File
@@ -2,6 +2,11 @@
Backend API for Copenhagen Truck Wash services.
Changes are published from a scoped feature branch through a pull request to
`master`; direct default-branch pushes are not part of the release workflow.
See [default branch protection](.github/BRANCH_PROTECTION.md) for the CI gate
and emergency procedure.
## Architecture & Stack
- **Edge Proxy:** [Traefik 2.11](https://doc.traefik.io/traefik/) (Handles TLS termination, routing, and rate limiting).
- **Web Server:** [Caddy 2.7](https://caddyserver.com/) (Serves the PHP application via FastCGI).
@@ -26,6 +31,32 @@ The API is accessible at:
- `https://localhost` (using Traefik default cert)
- `http(s)://localhost/api/` (proxied with `/api` prefix stripped)
### Test Gateway Container
To run a real PHP edge agent as a disposable Dockerized test gateway against the local stack, first create an install token from the edge gateway admin UI, then start the helper:
```powershell
.\scripts\test-gateway.ps1 start --install-token <token>
```
```bash
./scripts/test-gateway.sh start --install-token <token>
```
The helper will:
- start the local compose dependencies if needed
- claim a gateway through `http://localhost/api/edge-agent/claim`
- write the generated config to `.tmp/test-gateway/test-gateway.json`
- build `services/edge-agent/Dockerfile.test-gateway`
- run the PHP agent container on the local compose network
Useful follow-up commands:
```powershell
.\scripts\test-gateway.ps1 logs
.\scripts\test-gateway.ps1 status
.\scripts\test-gateway.ps1 stop
```
To start all services including multiple PHP workers and development tools (Jaeger, Portainer):
```powershell
docker compose --profile dev up -d
@@ -37,6 +68,18 @@ Configuration is primarily managed via environment variables.
- `services/nginx/app/config.php` loads configuration from the environment (requires `USE_ENV=true`).
- `php1` performs an automatic `composer install` on startup if `AUTO_COMPOSER_INSTALL=true`.
#### Environment Change Runbook
When updating `.env` values used by PHP containers (for example e-conomic tokens), recreate affected services so Docker applies the new env:
```powershell
docker compose up -d --force-recreate php1 php2 php3 php4 php5 php-cron
```
#### Edge Broker Public URL
Set `EDGE_PUBLIC_BROKER_URL` to the public route that serves the edge broker, including the path prefix handled by the proxy. Local Traefik uses `http://localhost/api/edge-broker`; production routes use the public broker prefix, for example `https://api.truckwash.dk/edge-broker`.
The browser terminal connects to the exact advertised `EDGE_PUBLIC_BROKER_URL` plus `/ws/browser-shell`. That URL must be routable through the proxy to the edge-broker service. Do not rely on derived `/api/edge-broker` fallback paths outside the local Traefik setup.
## Testing
The project now uses [Pest](https://pestphp.com/) as the primary test runner in `services/nginx/app`.
@@ -48,9 +91,20 @@ All commands are run from `services/nginx/app`:
composer test
composer test:unit
composer test:integration
composer test:api
composer test:coverage
```
For local Docker development, run the PHP suites inside `php1`:
```powershell
docker exec php1 sh -lc "cd /var/www/html && composer test:unit"
docker exec php1 sh -lc "cd /var/www/html && composer test:integration"
docker exec php1 sh -lc "cd /var/www/html && composer test:api"
docker exec php1 sh -lc "cd /var/www/html && composer run-script test:api:edge"
docker exec php1 sh -lc "cd /var/www/html && composer run-script test:integration:edge"
```
Integration tests are opt-in and should be run with required services available:
```powershell
@@ -58,6 +112,66 @@ $env:RUN_INTEGRATION_TESTS='1'
composer test:integration
```
### Edge Gateway Regression Coverage
The dedicated backend regression lane for the PHP edge gateway stack is split into:
- API contract tests for operator, agent, and broker-facing routes
- DB-backed integration tests for install sessions, heartbeats, tasks, logs, statistics, and shell persistence
- a local dockerized smoke that runs the real PHP edge agent against the local backend and broker
Run the targeted PHP suites inside `php1`:
```powershell
docker exec php1 sh -lc "cd /var/www/html && composer run-script test:api:edge"
docker exec php1 sh -lc "cd /var/www/html && composer run-script test:integration:edge"
```
Run the full local smoke from `backend-php` on the host:
```powershell
node .\scripts\edge-gateway-e2e.mjs
```
The E2E smoke expects the local compose stack and Docker daemon to be available. It boots a disposable gateway container, waits for a real heartbeat, validates live operations and telemetry, and verifies browser shell transcript persistence.
### Public Staging Edge-Gateway Smoke
`api.truckwash.io:4433` is the public staging ingress. For Edge Gateways v2, the router must serve the canonical artifacts from `services/nginx/app/resources/edge-gateway-agent`, not from the legacy `dist/agent.mjs` output or a separate runtime mount.
To verify the public staging stack after a deploy, use a real installer token and run:
```powershell
node .\scripts\staging-edge-gateway-smoke.mjs --install-token <token>
```
```bash
node ./scripts/staging-edge-gateway-smoke.mjs --install-token <token>
```
The smoke check fails unless all of these return `200` from the public domain:
- `/ping`
- `/edge-agent/artifacts/agent.php`
- `/edge-agent/artifacts/truckwash-edge-agent.service`
- `/edge-agent/install.sh?token=<real token>`
### API Test Suite
The `Api` suite exercises real HTTP endpoints instead of calling route handlers in-process.
- `composer test:api` enables `RUN_API_TESTS=1` automatically.
- By default the suite starts a temporary PHP server with `php -S 127.0.0.1:18080 index.php` and hits the app over HTTP.
- The suite covers the real router, request parsing, auth headers, status codes, and JSON response envelopes.
- Tests run serially and use explicit database/Redis fixtures plus reverse-order cleanup instead of transaction rollbacks.
- The default phase-1 coverage includes `/ping`, auth session routes, departments, department categories, and orders CRUD including the legacy `PUT /order` alias.
- API server logs are written to `services/nginx/app/build/logs/api-server.out.log` and `services/nginx/app/build/logs/api-server.err.log`.
To point the suite at an already-running base URL instead of the self-started PHP server:
```powershell
$env:API_TEST_BASE_URL='http://127.0.0.1:18080'
composer test:api
```
The suite requires an initialized application schema. Redis-backed flows are used when Redis is configured, but the suite can still boot without `caddy` or the full reverse-proxy stack.
### Run Tests Against A Cloned Live DB (Docker-Isolated)
Use the helper scripts in `scripts/` to:
1. Clone the configured live DB into a local MySQL Docker container.
@@ -96,6 +210,8 @@ FORCE=1 ./scripts/clone-live-to-debug-db.sh
### Test Layout
- `tests/Unit/*`: isolated unit and route-level behavior tests.
- `tests/Integration/*`: Redis/DB-backed tests intended for Docker/CI environments.
- `tests/Api/*`: real HTTP endpoint tests that boot a temporary PHP server and assert full request/response behavior.
- `tests/Api/api_coverage_manifest.php`: selected phase-1 endpoint manifest used by the API meta-test to enforce happy-path and failure coverage.
- `tests/<legacy-domain>/*`: legacy procedural scripts retained during migration; keep them runnable until matching Pest coverage exists.
## Logs & Monitoring
@@ -106,4 +222,22 @@ FORCE=1 ./scripts/clone-live-to-debug-db.sh
## API Documentation
- **OpenAPI:** The authoritative OpenAPI 3.0 contract is at `openapi.yaml`.
- **Self-Serve Module Guide:** Implementation and API guide at `services/nginx/app/modules/selfserve/selfserve.md`.
- **Writerside:** Documentation projects are located in `/Writerside` and `/Writerside2`.
- **Generated Writerside API Reference:** The active Writerside project lives in `/documentation` and is generated from `openapi.yaml`.
### Writerside OpenAPI Generation Workflow
Prerequisite:
- Python 3 with PyYAML (`pip install pyyaml`)
Run from repository root:
```powershell
python scripts/generate_writerside_openapi_docs.py generate
python scripts/generate_writerside_openapi_docs.py check
```
Contribution rule:
1. Update `openapi.yaml`.
2. Regenerate docs (`python scripts/generate_writerside_openapi_docs.py generate`).
3. Verify (`python scripts/generate_writerside_openapi_docs.py check`).
+76
View File
@@ -0,0 +1,76 @@
# Copenhagen Truck Wash API — Writerside Style Guide
# This file defines the style rules for our documentation.
# For more details, see https://vale.sh/docs/topics/styles/#extension-points
# --- RULE: Encourage descriptive language (existence) ---
extends: existence
message: "Avoid using '%s'. Try to be more descriptive or direct."
level: warning
ignorecase: true
tokens:
- simply
- just
- easy
- easily
- simple
- basically
- obviously
- actually
- very
- really
- pretty
- quite
- rather
---
# --- RULE: Flag incomplete documentation (existence) ---
extends: existence
message: "Incomplete documentation: '%s' found. Please provide comprehensive details."
level: error
ignorecase: true
tokens:
- TBD
- TO BE DETERMINED
- TODO
- FIXME
- Placeholder
- Coming soon
---
# --- RULE: Preferred terminology (substitution) ---
extends: substitution
message: "Consider using '%s' instead of its informal or less descriptive counterpart."
level: suggestion
ignorecase: true
swap:
check[ -]box: checkbox
right-click menu: context menu|popup menu
webpage: page
click on: click|select
press: press|select
setup: set up
log[ -]in: sign in|log in
e-mail: email
interface: UI|interface
the following: :
utilize: use
functionality: feature|function
additional: more|extra
---
# --- RULE: Avoid jargon and filler (existence) ---
extends: existence
message: "Avoid jargon or filler phrases like '%s'."
level: warning
ignorecase: true
tokens:
- leverage
- bandwidth
- synergy
- best-in-class
- cutting-edge
- robust
- state-of-the-art
- mission-critical
- go-forward
- paradigm shift
BIN
View File
Binary file not shown.
+30 -7
View File
@@ -4,12 +4,13 @@ $CONFIG_DB = [
'user' => '', // Username of the database server e.g. root
'password' => '', // Password of the database server e.g. password123
'database' => '', // Name of the database e.g. my_database
'port' => 3306, // Port of the database server e.g. 3306
'ssl_mode' => 'DISABLED' // SSL mode for mysqldump: DISABLED, PREFERRED, REQUIRED, VERIFY_CA, VERIFY_IDENTITY
];
$DEBUG = true; // Set to true to enable debugging (Error messages will be shown, and this should never be used in production)
$USE_PROD_ECONOMIC_IN_DEBUG = true; // Set to true to use the production economic API in debug mode
$ENCRYPTION_KEY = ''; // 44 Characters long encryption key
$CORS = '*'; // Set to the domain that should be allowed to access the API e.g. https://example.com
$CORS = '*'; // Set to comma-separated allowed origins e.g. https://example.com,https://api-v2.truckwash.io
$ECONOMIC_API = [
'app_access_grant' => '', // Economic API access grant token (1)
'app_access_grant2' => '', // Economic API access grant token (2)
@@ -30,11 +31,13 @@ $MINIO = [
'access_key' => '', // Minio access
'secret_key' => '' // Minio secret key
];
$SLACK_DEFAULT_WEBHOOK = ''; // Default Slack webhook URL e.g. https://hooks.slack.com/services/XXXXXXXXX/XXXXXXXXX/XXXXXXXXXXXXXXXXXXXXXXXX
$SLACK_DEFAULT_WEBHOOK = ''; // Set through SLACK_DEFAULT_WEBHOOK; never commit a production webhook URL.
$REDIS_CONFIG = [
'host' => '', // Redis host (IP address)
'user' => '', // Redis user
'database' => 0, // Redis database number (0-15)
'password' => '' // Redis password
'password' => '', // Redis password
'port' => 6379 // Redis port
];
// Set the timezone
@@ -50,6 +53,7 @@ if (isset($_ENV['USE_ENV']) && $_ENV['USE_ENV'] === 'true') {
'CONFIG_DB_USER' => 'user',
'CONFIG_DB_PASSWORD' => 'password',
'CONFIG_DB_DATABASE' => 'database',
'CONFIG_DB_PORT' => 'port',
'CONFIG_DB_SSL_MODE' => 'ssl_mode',
'DEBUG' => 'DEBUG',
'ENCRYPTION_KEY' => 'ENCRYPTION_KEY',
@@ -66,7 +70,10 @@ if (isset($_ENV['USE_ENV']) && $_ENV['USE_ENV'] === 'true') {
'SLACK_DEFAULT_WEBHOOK' => 'SLACK_DEFAULT_WEBHOOK',
'REDIS_CONFIG_HOST' => 'host',
'REDIS_CONFIG_DATABASE' => 'database',
'REDIS_CONFIG_PASSWORD' => 'password'
'REDIS_CONFIG_PASSWORD' => 'password',
'REDIS_CONFIG_PORT' => 'port',
'REDIS_CONFIG_USER' => 'user',
'REDIS_CONFIG_DEBUG_PASSWORD' => 'debug_password'
];
$dbTarget = strtolower(trim((string)($_ENV['CONFIG_DB_TARGET'] ?? 'live')));
if ($dbTarget !== 'live' && $dbTarget !== 'debug') {
@@ -94,6 +101,7 @@ if (isset($_ENV['USE_ENV']) && $_ENV['USE_ENV'] === 'true') {
'user' => $resolveDbValue('USER'),
'password' => $resolveDbValue('PASSWORD'),
'database' => $resolveDbValue('DATABASE'),
'port' => (int)($resolveDbValue('PORT') ?: 3306),
'ssl_mode' => $resolveDbValue('SSL_MODE') !== '' ? $resolveDbValue('SSL_MODE') : 'DISABLED'
];
/**
@@ -140,13 +148,28 @@ if (isset($_ENV['USE_ENV']) && $_ENV['USE_ENV'] === 'true') {
* Set the Slack default webhook
*/
$SLACK_DEFAULT_WEBHOOK = $_ENV['SLACK_DEFAULT_WEBHOOK'];
$resolveRedisValue = function (string $key) use ($dbTarget): string {
$liveKey = 'REDIS_CONFIG_' . $key;
$debugKey = 'REDIS_CONFIG_DEBUG_' . $key;
$liveValue = (string)($_ENV[$liveKey] ?? '');
$debugValue = (string)($_ENV[$debugKey] ?? '');
if ($dbTarget === 'debug' && $debugValue !== '') {
return $debugValue;
}
return $liveValue;
};
/**
* Set the Redis configuration
*/
$REDIS_CONFIG = [
'host' => $_ENV['REDIS_CONFIG_HOST'],
'database' => $_ENV['REDIS_CONFIG_DATABASE'],
'password' => $_ENV['REDIS_CONFIG_PASSWORD']
'host' => $resolveRedisValue('HOST'),
'user' => $resolveRedisValue('USER'),
'database' => $resolveRedisValue('DATABASE'),
'password' => $resolveRedisValue('PASSWORD'),
'port' => (int)($resolveRedisValue('PORT') ?: 6379)
];
// Set the timezone
+34 -2
View File
@@ -46,6 +46,32 @@ services:
timeout: 5s
retries: 10
edge-broker:
build:
context: .
dockerfile: services/edge-broker/Dockerfile
container_name: edge-broker
environment:
EDGE_AUTH_MODE: ${EDGE_AUTH_MODE:-strict}
EDGE_MANAGER_URL: ${EDGE_MANAGER_URL:-http://caddy}
EDGE_BROKER_SHARED_SECRET: ${EDGE_BROKER_SHARED_SECRET:?set EDGE_BROKER_SHARED_SECRET in .env}
labels:
- "traefik.enable=true"
- "traefik.http.routers.edge-broker-api.rule=Host(`api.example.com`) && PathPrefix(`/edge-broker`)"
- "traefik.http.routers.edge-broker-api.entrypoints=websecure"
- "traefik.http.routers.edge-broker-api.tls=true"
- "traefik.http.routers.edge-broker-api.tls.certresolver=le"
- "traefik.http.routers.edge-broker-api.middlewares=edge-broker-strip"
- "traefik.http.routers.edge-broker-api.service=edge-broker"
- "traefik.http.routers.edge-broker-local.rule=Host(`localhost`) && PathPrefix(`/api/edge-broker`)"
- "traefik.http.routers.edge-broker-local.entrypoints=web"
- "traefik.http.routers.edge-broker-local.middlewares=edge-broker-strip-local"
- "traefik.http.routers.edge-broker-local.service=edge-broker"
- "traefik.http.middlewares.edge-broker-strip.stripPrefix.prefixes=/edge-broker"
- "traefik.http.middlewares.edge-broker-strip-local.stripPrefix.prefixes=/api/edge-broker"
- "traefik.http.services.edge-broker.loadbalancer.server.port=4300"
caddy:
image: caddy:2.7.6-alpine
container_name: caddy
@@ -81,11 +107,14 @@ services:
depends_on:
- redis
- mysql
- edge-broker
command: ["php-fpm"]
env_file:
- .env.example
environment:
AUTO_COMPOSER_INSTALL: "true"
EDGE_BROKER_URL: ${EDGE_BROKER_URL:-http://edge-broker:4300}
EDGE_BROKER_SHARED_SECRET: ${EDGE_BROKER_SHARED_SECRET:?set EDGE_BROKER_SHARED_SECRET in .env}
volumes:
- ./services/nginx/app:/var/www/html
- ./services/php/php.ini:/usr/local/etc/php/conf.d/zz-custom.ini:ro
@@ -99,11 +128,14 @@ services:
depends_on:
- redis
- mysql
command: ["sh", "-c", "while true; do php index.php run cron; sleep 60; done"]
- edge-broker
command: ["php", "index.php", "run", "cron-worker"]
env_file:
- .env.example
environment:
AUTO_COMPOSER_INSTALL: "false"
EDGE_BROKER_URL: ${EDGE_BROKER_URL:-http://edge-broker:4300}
EDGE_BROKER_SHARED_SECRET: ${EDGE_BROKER_SHARED_SECRET:?set EDGE_BROKER_SHARED_SECRET in .env}
volumes:
- ./services/nginx/app:/var/www/html
- ./services/php/php.ini:/usr/local/etc/php/conf.d/zz-custom.ini:ro
@@ -140,4 +172,4 @@ services:
volumes:
mysql_data:
redis_data:
n8n_data:
n8n_data:
+440
View File
@@ -0,0 +1,440 @@
services:
traefik:
image: traefik:2.11
container_name: traefik
ports:
- "80:80"
- "443:443"
- "4433:4433"
- "9100:9100"
volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro
- ./services/traefik/traefik.yml:/etc/traefik/traefik.yml:ro
- ./services/traefik/dynamic.yml:/etc/traefik/dynamic.yml:ro
- ./services/traefik/acme.json:/acme.json
- ./services/traefik/acme-io.json:/acme-io.json
labels:
- "traefik.enable=true"
- "traefik.http.routers.traefik.rule=Host(`traefik.truckwash.dk`)"
- "traefik.http.routers.traefik.entrypoints=websecure"
- "traefik.http.routers.traefik.tls=true"
- "traefik.http.routers.traefik.tls.certresolver=le"
- "traefik.http.routers.traefik.service=api@internal"
- "traefik.http.routers.traefik.middlewares=dashboard-allow-local@file,dashboard-auth@file"
- "traefik.http.routers.traefik-http.rule=Host(`traefik.truckwash.dk`)"
- "traefik.http.routers.traefik-http.entrypoints=web"
- "traefik.http.routers.traefik-http.middlewares=redirect-to-https@file"
- "traefik.http.routers.traefik-http.service=api@internal"
- "traefik.http.routers.traefik-local.rule=Host(`traefik.localhost`)"
- "traefik.http.routers.traefik-local.entrypoints=web"
- "traefik.http.routers.traefik-local.service=api@internal"
- "traefik.http.routers.traefik-local.middlewares=dashboard-allow-local@file,dashboard-auth@file"
redis:
image: redis:7
container_name: redis
volumes:
- nnks_redis:/data
healthcheck:
test: ["CMD", "redis-cli", "ping"]
interval: 10s
timeout: 5s
retries: 5
start_period: 10s
redis-staging:
image: redis:7
container_name: redis-staging
volumes:
- nnks_redis_staging:/data
healthcheck:
test: ["CMD", "redis-cli", "ping"]
interval: 10s
timeout: 5s
retries: 5
start_period: 10s
mysql-debug:
image: mysql:8.4
container_name: mysql-debug
environment:
MYSQL_ROOT_PASSWORD: ${CONFIG_DB_DEBUG_PASSWORD:-debug_root_password}
MYSQL_DATABASE: ${CONFIG_DB_DEBUG_DATABASE:-nnks_db_debug}
ports:
- "3307:3306"
volumes:
- db_debug_data:/var/lib/mysql
healthcheck:
test: ["CMD-SHELL", "MYSQL_PWD=$$MYSQL_ROOT_PASSWORD mysqladmin -u root ping --silent"]
interval: 10s
timeout: 5s
retries: 10
start_period: 20s
edge-broker:
build:
context: .
dockerfile: services/edge-broker/Dockerfile
container_name: edge-broker
environment:
EDGE_AUTH_MODE: ${EDGE_AUTH_MODE:-manager}
EDGE_MANAGER_URL: ${EDGE_MANAGER_URL:-http://caddy}
EDGE_BROKER_SHARED_SECRET: ${EDGE_BROKER_SHARED_SECRET:-truckwash-edge-dev}
labels:
- "traefik.enable=true"
- "traefik.http.routers.edge-broker-api.rule=Host(`api.truckwash.dk`) && PathPrefix(`/edge-broker`)"
- "traefik.http.routers.edge-broker-api.entrypoints=websecure"
- "traefik.http.routers.edge-broker-api.tls=true"
- "traefik.http.routers.edge-broker-api.tls.certresolver=le"
- "traefik.http.routers.edge-broker-api.middlewares=secure-headers@file,edge-broker-strip"
- "traefik.http.routers.edge-broker-api.priority=200"
- "traefik.http.routers.edge-broker-api.service=edge-broker"
- "traefik.http.routers.edge-broker-api-io.rule=Host(`api.truckwash.io`) && PathPrefix(`/edge-broker`)"
- "traefik.http.routers.edge-broker-api-io.entrypoints=websecure"
- "traefik.http.routers.edge-broker-api-io.tls=true"
- "traefik.http.routers.edge-broker-api-io.tls.certresolver=le_io"
- "traefik.http.routers.edge-broker-api-io.middlewares=secure-headers@file,edge-broker-strip"
- "traefik.http.routers.edge-broker-api-io.priority=200"
- "traefik.http.routers.edge-broker-api-io.service=edge-broker"
- "traefik.http.routers.edge-broker-api-v2.rule=Host(`api-v2.truckwash.io`) && PathPrefix(`/edge-broker`)"
- "traefik.http.routers.edge-broker-api-v2.entrypoints=websecure"
- "traefik.http.routers.edge-broker-api-v2.tls=true"
- "traefik.http.routers.edge-broker-api-v2.tls.certresolver=le_io"
- "traefik.http.routers.edge-broker-api-v2.middlewares=secure-headers@file,edge-broker-strip"
- "traefik.http.routers.edge-broker-api-v2.priority=200"
- "traefik.http.routers.edge-broker-api-v2.service=edge-broker"
- "traefik.http.routers.edge-broker-api-staging.rule=Host(`api.truckwash.io`) && PathPrefix(`/edge-broker`)"
- "traefik.http.routers.edge-broker-api-staging.entrypoints=websecure-staging"
- "traefik.http.routers.edge-broker-api-staging.tls=true"
- "traefik.http.routers.edge-broker-api-staging.tls.certresolver=le_io"
- "traefik.http.routers.edge-broker-api-staging.middlewares=secure-headers@file,edge-broker-strip"
- "traefik.http.routers.edge-broker-api-staging.priority=200"
- "traefik.http.routers.edge-broker-api-staging.service=edge-broker"
- "traefik.http.routers.edge-broker-local.rule=Host(`localhost`) && PathPrefix(`/api/edge-broker`)"
- "traefik.http.routers.edge-broker-local.entrypoints=web"
- "traefik.http.routers.edge-broker-local.middlewares=secure-headers@file,edge-broker-strip-local"
- "traefik.http.routers.edge-broker-local.priority=200"
- "traefik.http.routers.edge-broker-local.service=edge-broker"
- "traefik.http.routers.edge-broker-local-secure.rule=Host(`localhost`) && PathPrefix(`/api/edge-broker`)"
- "traefik.http.routers.edge-broker-local-secure.entrypoints=websecure"
- "traefik.http.routers.edge-broker-local-secure.tls=true"
- "traefik.http.routers.edge-broker-local-secure.middlewares=secure-headers@file,edge-broker-strip-local"
- "traefik.http.routers.edge-broker-local-secure.priority=200"
- "traefik.http.routers.edge-broker-local-secure.service=edge-broker"
- "traefik.http.routers.edge-broker-local-staging.rule=Host(`localhost`) && PathPrefix(`/api/edge-broker`)"
- "traefik.http.routers.edge-broker-local-staging.entrypoints=websecure-staging"
- "traefik.http.routers.edge-broker-local-staging.tls=true"
- "traefik.http.routers.edge-broker-local-staging.middlewares=secure-headers@file,edge-broker-strip-local"
- "traefik.http.routers.edge-broker-local-staging.priority=200"
- "traefik.http.routers.edge-broker-local-staging.service=edge-broker"
- "traefik.http.middlewares.edge-broker-strip.stripPrefix.prefixes=/edge-broker"
- "traefik.http.middlewares.edge-broker-strip-local.stripPrefix.prefixes=/api/edge-broker"
- "traefik.http.services.edge-broker.loadbalancer.server.port=4300"
caddy:
image: caddy:2.7.6-alpine
container_name: caddy
depends_on:
- php1
- php2
- php3
- php4
- php5
volumes:
- ./services/nginx/app:/var/www/html
- ./services/caddy/Caddyfile:/etc/caddy/Caddyfile:ro
- ./services/caddy/logs:/var/log/caddy
labels:
- "traefik.enable=true"
- "traefik.http.routers.api.rule=Host(`api.truckwash.dk`)"
- "traefik.http.routers.api.entrypoints=websecure"
- "traefik.http.routers.api.tls=true"
- "traefik.http.routers.api.tls.domains[0].main=api.truckwash.dk"
- "traefik.http.routers.api.tls.certresolver=le"
- "traefik.http.routers.api.service=caddy"
- "traefik.http.routers.api.middlewares=secure-headers@file,api-ratelimit@file"
- "traefik.http.routers.api-io.rule=Host(`api.truckwash.io`)"
- "traefik.http.routers.api-io.entrypoints=websecure"
- "traefik.http.routers.api-io.tls=true"
- "traefik.http.routers.api-io.tls.domains[0].main=api.truckwash.io"
- "traefik.http.routers.api-io.tls.certresolver=le_io"
- "traefik.http.routers.api-io.service=caddy"
- "traefik.http.routers.api-io.middlewares=secure-headers@file,api-ratelimit@file"
- "traefik.http.routers.api-v2.rule=Host(`api-v2.truckwash.io`)"
- "traefik.http.routers.api-v2.entrypoints=websecure"
- "traefik.http.routers.api-v2.tls=true"
- "traefik.http.routers.api-v2.tls.domains[0].main=api-v2.truckwash.io"
- "traefik.http.routers.api-v2.tls.certresolver=le_io"
- "traefik.http.routers.api-v2.service=caddy"
- "traefik.http.routers.api-v2.middlewares=secure-headers@file,api-ratelimit@file"
- "traefik.http.routers.api-http.rule=Host(`api.truckwash.dk`) || Host(`api.truckwash.io`) || Host(`api-v2.truckwash.io`)"
- "traefik.http.routers.api-http.entrypoints=web"
- "traefik.http.routers.api-http.middlewares=redirect-to-https@file"
- "traefik.http.routers.api-http.service=caddy"
- "traefik.http.routers.local.rule=Host(`localhost`)"
- "traefik.http.routers.local.entrypoints=web"
- "traefik.http.routers.local.service=caddy"
- "traefik.http.routers.local.middlewares=secure-headers@file"
- "traefik.http.routers.local-secure.rule=Host(`localhost`)"
- "traefik.http.routers.local-secure.entrypoints=websecure"
- "traefik.http.routers.local-secure.tls=true"
- "traefik.http.routers.local-secure.service=caddy"
- "traefik.http.routers.local-secure.middlewares=secure-headers@file"
- "traefik.http.routers.local-api.rule=Host(`localhost`) && PathPrefix(`/api`)"
- "traefik.http.routers.local-api.entrypoints=web"
- "traefik.http.routers.local-api.middlewares=strip-api-prefix@file,secure-headers@file"
- "traefik.http.routers.local-api.service=caddy"
- "traefik.http.routers.local-api.priority=100"
- "traefik.http.routers.local-api-secure.rule=Host(`localhost`) && PathPrefix(`/api`)"
- "traefik.http.routers.local-api-secure.entrypoints=websecure"
- "traefik.http.routers.local-api-secure.tls=true"
- "traefik.http.routers.local-api-secure.middlewares=strip-api-prefix@file,secure-headers@file"
- "traefik.http.routers.local-api-secure.service=caddy"
- "traefik.http.routers.local-api-secure.priority=100"
- "traefik.http.services.caddy.loadbalancer.server.port=80"
caddy-staging:
image: caddy:2.7.6-alpine
container_name: caddy-staging
depends_on:
- php-staging
volumes:
- ./services/nginx/staging:/var/www/html
- ./services/caddy/Caddyfile-staging:/etc/caddy/Caddyfile:ro
- ./services/caddy/logs-staging:/var/log/caddy
labels:
- "traefik.enable=true"
- "traefik.http.routers.api-staging.rule=Host(`api.truckwash.io`)"
- "traefik.http.routers.api-staging.entrypoints=websecure-staging"
- "traefik.http.routers.api-staging.tls=true"
- "traefik.http.routers.api-staging.tls.domains[0].main=api.truckwash.io"
- "traefik.http.routers.api-staging.tls.certresolver=le_io"
- "traefik.http.routers.api-staging.service=caddy-staging"
- "traefik.http.routers.api-staging.middlewares=secure-headers@file,api-ratelimit@file"
- "traefik.http.routers.local-staging.rule=Host(`localhost`)"
- "traefik.http.routers.local-staging.entrypoints=websecure-staging"
- "traefik.http.routers.local-staging.service=caddy-staging"
- "traefik.http.routers.local-staging.middlewares=secure-headers@file"
- "traefik.http.routers.local-staging-secure.rule=Host(`localhost`)"
- "traefik.http.routers.local-staging-secure.entrypoints=websecure-staging"
- "traefik.http.routers.local-staging-secure.tls=true"
- "traefik.http.routers.local-staging-secure.service=caddy-staging"
- "traefik.http.routers.local-staging-secure.middlewares=secure-headers@file"
- "traefik.http.routers.local-staging-api.rule=Host(`localhost`) && PathPrefix(`/api`)"
- "traefik.http.routers.local-staging-api.entrypoints=websecure-staging"
- "traefik.http.routers.local-staging-api.middlewares=strip-api-prefix@file,secure-headers@file"
- "traefik.http.routers.local-staging-api.service=caddy-staging"
- "traefik.http.routers.local-staging-api.priority=100"
- "traefik.http.routers.local-staging-api-secure.rule=Host(`localhost`) && PathPrefix(`/api`)"
- "traefik.http.routers.local-staging-api-secure.entrypoints=websecure-staging"
- "traefik.http.routers.local-staging-api-secure.tls=true"
- "traefik.http.routers.local-staging-api-secure.middlewares=strip-api-prefix@file,secure-headers@file"
- "traefik.http.routers.local-staging-api-secure.service=caddy-staging"
- "traefik.http.routers.local-staging-api-secure.priority=100"
- "traefik.http.services.caddy-staging.loadbalancer.server.port=80"
php1:
build:
context: .
dockerfile: services/php/Dockerfile
container_name: php1
depends_on:
- redis
- edge-broker
command: ["php-fpm"]
env_file:
- .env
environment:
AUTO_COMPOSER_INSTALL: "true"
EDGE_BROKER_URL: ${EDGE_BROKER_URL:-http://edge-broker:4300}
EDGE_BROKER_SHARED_SECRET: ${EDGE_BROKER_SHARED_SECRET:-truckwash-edge-dev}
volumes:
- ./services/nginx/app:/var/www/html
- ./services/edge-agent/dist:/services/edge-agent/dist:ro
- ./services/php/php.ini:/usr/local/etc/php/conf.d/zz-custom.ini:ro
- ./services/php/logs:/var/log/php
php2:
build:
context: .
dockerfile: services/php/Dockerfile
container_name: php2
depends_on:
- redis
- edge-broker
command: ["php-fpm"]
env_file:
- .env
environment:
AUTO_COMPOSER_INSTALL: "false"
EDGE_BROKER_URL: ${EDGE_BROKER_URL:-http://edge-broker:4300}
EDGE_BROKER_SHARED_SECRET: ${EDGE_BROKER_SHARED_SECRET:-truckwash-edge-dev}
volumes:
- ./services/nginx/app:/var/www/html
- ./services/edge-agent/dist:/services/edge-agent/dist:ro
- ./services/php/php.ini:/usr/local/etc/php/conf.d/zz-custom.ini:ro
- ./services/php/logs:/var/log/php
php3:
build:
context: .
dockerfile: services/php/Dockerfile
container_name: php3
depends_on:
- redis
- edge-broker
command: ["php-fpm"]
env_file:
- .env
environment:
AUTO_COMPOSER_INSTALL: "false"
EDGE_BROKER_URL: ${EDGE_BROKER_URL:-http://edge-broker:4300}
EDGE_BROKER_SHARED_SECRET: ${EDGE_BROKER_SHARED_SECRET:-truckwash-edge-dev}
volumes:
- ./services/nginx/app:/var/www/html
- ./services/edge-agent/dist:/services/edge-agent/dist:ro
- ./services/php/php.ini:/usr/local/etc/php/conf.d/zz-custom.ini:ro
- ./services/php/logs:/var/log/php
php4:
build:
context: .
dockerfile: services/php/Dockerfile
container_name: php4
depends_on:
- redis
- edge-broker
command: ["php-fpm"]
env_file:
- .env
environment:
AUTO_COMPOSER_INSTALL: "false"
EDGE_BROKER_URL: ${EDGE_BROKER_URL:-http://edge-broker:4300}
EDGE_BROKER_SHARED_SECRET: ${EDGE_BROKER_SHARED_SECRET:-truckwash-edge-dev}
volumes:
- ./services/nginx/app:/var/www/html
- ./services/edge-agent/dist:/services/edge-agent/dist:ro
- ./services/php/php.ini:/usr/local/etc/php/conf.d/zz-custom.ini:ro
- ./services/php/logs:/var/log/php
php5:
build:
context: .
dockerfile: services/php/Dockerfile
container_name: php5
depends_on:
- redis
- edge-broker
command: ["php-fpm"]
env_file:
- .env
environment:
AUTO_COMPOSER_INSTALL: "false"
EDGE_BROKER_URL: ${EDGE_BROKER_URL:-http://edge-broker:4300}
EDGE_BROKER_SHARED_SECRET: ${EDGE_BROKER_SHARED_SECRET:-truckwash-edge-dev}
volumes:
- ./services/nginx/app:/var/www/html
- ./services/edge-agent/dist:/services/edge-agent/dist:ro
- ./services/php/php.ini:/usr/local/etc/php/conf.d/zz-custom.ini:ro
- ./services/php/logs:/var/log/php
php-staging:
build:
context: .
dockerfile: services/php/Dockerfile
container_name: php-staging
depends_on:
- redis-staging
- edge-broker
command: ["php-fpm"]
env_file:
- .env.staging
environment:
AUTO_COMPOSER_INSTALL: "false"
EDGE_BROKER_URL: ${EDGE_BROKER_URL:-http://edge-broker:4300}
EDGE_BROKER_SHARED_SECRET: ${EDGE_BROKER_SHARED_SECRET:-truckwash-edge-dev}
volumes:
- ./services/nginx/staging:/var/www/html
- ./services/edge-agent/dist:/services/edge-agent/dist:ro
- ./services/php/php.ini:/usr/local/etc/php/conf.d/zz-custom.ini:ro
- ./services/php/logs-staging:/var/log/php
php-cron:
build:
context: .
dockerfile: services/php/Dockerfile
container_name: php-cron
depends_on:
- redis
- edge-broker
command: ["php", "index.php", "run", "cron-worker"]
env_file:
- .env
environment:
AUTO_COMPOSER_INSTALL: "false"
EDGE_BROKER_URL: ${EDGE_BROKER_URL:-http://edge-broker:4300}
EDGE_BROKER_SHARED_SECRET: ${EDGE_BROKER_SHARED_SECRET:-truckwash-edge-dev}
volumes:
- ./services/nginx/app:/var/www/html
- ./services/edge-agent/dist:/services/edge-agent/dist:ro
- ./services/php/php.ini:/usr/local/etc/php/conf.d/zz-custom.ini:ro
- ./services/php/logs:/var/log/php
portainer:
image: portainer/portainer-ce:2.21.4
container_name: portainer
profiles:
- dev
ports:
- "9443:9443"
- "9000:9000"
volumes:
- /var/run/docker.sock:/var/run/docker.sock
- portainer_data:/data
jaeger:
image: jaegertracing/all-in-one:1.53
container_name: jaeger
profiles:
- dev
environment:
- COLLECTOR_ZIPKIN_HTTP_PORT=9411
ports:
- "16686:16686"
n8n:
image: n8nio/n8n:latest
container_name: n8n
restart: always
environment:
- N8N_HOST=n8n.truckwash.io
- N8N_PORT=5678
- N8N_PROTOCOL=https
- NODE_ENV=production
- WEBHOOK_URL=https://n8n.truckwash.io/
- GENERIC_TIMEZONE=${CONFIG_TIMEZONE:-Europe/Copenhagen}
volumes:
- n8n_data:/home/node/.n8n
labels:
- "traefik.enable=true"
- "traefik.http.routers.n8n.rule=Host(`n8n.truckwash.io`)"
- "traefik.http.routers.n8n.entrypoints=websecure"
- "traefik.http.routers.n8n.tls=true"
- "traefik.http.routers.n8n.tls.certresolver=le_io"
- "traefik.http.routers.n8n.service=n8n"
- "traefik.http.routers.n8n-http.rule=Host(`n8n.truckwash.io`)"
- "traefik.http.routers.n8n-http.entrypoints=web"
- "traefik.http.routers.n8n-http.middlewares=redirect-to-https@file"
- "traefik.http.routers.n8n-http.service=n8n"
- "traefik.http.services.n8n.loadbalancer.server.port=5678"
volumes:
db_data:
db_debug_data:
nnks_redis:
nnks_redis_staging:
es_data:
portainer_data:
fleet-server-data:
elastic-agent-data:
n8n_data:
+2 -2
View File
@@ -27,5 +27,5 @@ services:
## docker compose up -d traefik caddy php1 php2 php3 php4 php5 db redis
##
## Notes:
## - Traefik uses Lets Encrypt production. Ensure DNS A/AAAA records for api.truckwash.dk, api.truckwash.io and traefik.truckwash.dk point to this host and ports 80/443 are reachable.
## - The dashboard is protected by basic auth and an IP allowlist (defined in dynamic.yml). Replace the bcrypt hash before enabling in production.
## - Traefik uses Lets Encrypt production. Ensure DNS A/AAAA records for api.truckwash.dk, api.truckwash.io, api-v2.truckwash.io and traefik.truckwash.dk point to the expected ingress and ports 80/443 are reachable.
## - The dashboard is protected by basic auth and an IP allowlist (defined in dynamic.yml). Replace the bcrypt hash before enabling in production.
+501
View File
@@ -0,0 +1,501 @@
services:
traefik:
image: traefik:2.11
container_name: traefik
group_add:
- "${DOCKER_SOCKET_GID:-65534}"
ports:
- "${TRAEFIK_WEB_PORT:-80}:80"
- "${TRAEFIK_WEBSECURE_PORT:-443}:443"
- "${TRAEFIK_WEBSECURE_STAGING_PORT:-4433}:4433"
# Prometheus metrics endpoint (local dev)
- "${TRAEFIK_METRICS_PORT:-9100}:9100"
volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro
- ./services/traefik/traefik.yml:/etc/traefik/traefik.yml:ro
- ./services/traefik/dynamic.yml:/etc/traefik/dynamic.yml:ro
- ./services/traefik/acme.json:/acme.json
- ./services/traefik/acme-io.json:/acme-io.json
labels:
- "traefik.enable=true"
# Dashboard over HTTPS (production)
- "traefik.http.routers.traefik.rule=Host(`traefik.truckwash.dk`)"
- "traefik.http.routers.traefik.entrypoints=websecure"
- "traefik.http.routers.traefik.tls=true"
- "traefik.http.routers.traefik.tls.certresolver=le"
- "traefik.http.routers.traefik.service=api@internal"
- "traefik.http.routers.traefik.middlewares=dashboard-allow-local@file,dashboard-auth@file"
# Dashboard over HTTP (dev) -> redirect to HTTPS
- "traefik.http.routers.traefik-http.rule=Host(`traefik.truckwash.dk`)"
- "traefik.http.routers.traefik-http.entrypoints=web"
- "traefik.http.routers.traefik-http.middlewares=redirect-to-https@file"
- "traefik.http.routers.traefik-http.service=api@internal"
# Local dashboard on traefik.localhost (HTTP only for dev)
- "traefik.http.routers.traefik-local.rule=Host(`traefik.localhost`)"
- "traefik.http.routers.traefik-local.entrypoints=web"
- "traefik.http.routers.traefik-local.service=api@internal"
- "traefik.http.routers.traefik-local.middlewares=dashboard-allow-local@file,dashboard-auth@file"
# Broker API (handled in edge-broker service)
- "traefik.http.routers.edge-broker.rule=Host(`api.truckwash.dk`) && PathPrefix(`/edge-broker`)"
- "traefik.http.routers.edge-broker.entrypoints=websecure"
- "traefik.http.routers.edge-broker.tls=true"
- "traefik.http.routers.edge-broker.tls.certresolver=le"
- "traefik.http.routers.edge-broker.middlewares=secure-headers@file,edge-broker-strip"
- "traefik.http.routers.edge-broker.priority=200"
- "traefik.http.routers.edge-broker.service=edge-broker"
- "traefik.http.routers.edge-broker-io.rule=Host(`api.truckwash.io`) && PathPrefix(`/edge-broker`)"
- "traefik.http.routers.edge-broker-io.entrypoints=websecure"
- "traefik.http.routers.edge-broker-io.tls=true"
- "traefik.http.routers.edge-broker-io.tls.certresolver=le_io"
- "traefik.http.routers.edge-broker-io.middlewares=secure-headers@file,edge-broker-strip"
- "traefik.http.routers.edge-broker-io.priority=200"
- "traefik.http.routers.edge-broker-io.service=edge-broker"
- "traefik.http.routers.edge-broker-v2.rule=Host(`api-v2.truckwash.io`) && PathPrefix(`/edge-broker`)"
- "traefik.http.routers.edge-broker-v2.entrypoints=websecure"
- "traefik.http.routers.edge-broker-v2.tls=true"
- "traefik.http.routers.edge-broker-v2.tls.certresolver=le_io"
- "traefik.http.routers.edge-broker-v2.middlewares=secure-headers@file,edge-broker-strip"
- "traefik.http.routers.edge-broker-v2.priority=200"
- "traefik.http.routers.edge-broker-v2.service=edge-broker"
- "traefik.http.routers.edge-broker-staging.rule=Host(`api.truckwash.io`) && PathPrefix(`/edge-broker`)"
- "traefik.http.routers.edge-broker-staging.entrypoints=websecure-staging"
- "traefik.http.routers.edge-broker-staging.tls=true"
- "traefik.http.routers.edge-broker-staging.tls.certresolver=le_io"
- "traefik.http.routers.edge-broker-staging.middlewares=secure-headers@file,edge-broker-strip"
- "traefik.http.routers.edge-broker-staging.priority=200"
- "traefik.http.routers.edge-broker-staging.service=edge-broker"
- "traefik.http.routers.edge-broker-local.rule=Host(`localhost`) && PathPrefix(`/api/edge-broker`)"
- "traefik.http.routers.edge-broker-local.entrypoints=web"
- "traefik.http.routers.edge-broker-local.middlewares=secure-headers@file,edge-broker-strip-local"
- "traefik.http.routers.edge-broker-local.priority=200"
- "traefik.http.routers.edge-broker-local.service=edge-broker"
- "traefik.http.routers.edge-broker-local-secure.rule=Host(`localhost`) && PathPrefix(`/api/edge-broker`)"
redis:
image: redis:7
container_name: redis
# ports:
# - "6379:6379"
volumes:
- nnks_redis:/data
healthcheck:
test: ["CMD", "redis-cli", "ping"]
interval: 10s
timeout: 5s
retries: 5
start_period: 10s
redis-staging:
image: redis:7
container_name: redis-staging
# ports:
# - "6380:6379"
volumes:
- nnks_redis_staging:/data
healthcheck:
test: ["CMD", "redis-cli", "ping"]
interval: 10s
timeout: 5s
retries: 5
start_period: 10s
mysql-debug:
image: mysql:8.4
container_name: mysql-debug
profiles: [dev]
command: ["mysqld", "--innodb-use-native-aio=0"]
environment:
MYSQL_ROOT_PASSWORD: ${CONFIG_DB_DEBUG_PASSWORD:?CONFIG_DB_DEBUG_PASSWORD is required for mysql-debug}
MYSQL_DATABASE: ${CONFIG_DB_DEBUG_DATABASE:-nnks_db_debug}
ports:
- "3307:3306"
volumes:
- db_debug_data:/var/lib/mysql
healthcheck:
test: ["CMD-SHELL", "MYSQL_PWD=$$MYSQL_ROOT_PASSWORD mysqladmin -u root ping --silent"]
interval: 10s
timeout: 5s
retries: 10
start_period: 20s
edge-broker:
build:
context: .
dockerfile: services/edge-broker/Dockerfile
container_name: edge-broker
environment:
EDGE_AUTH_MODE: ${EDGE_AUTH_MODE:-strict}
EDGE_MANAGER_URL: ${EDGE_MANAGER_URL:-http://caddy}
EDGE_BROKER_SHARED_SECRET: ${EDGE_BROKER_SHARED_SECRET:?set EDGE_BROKER_SHARED_SECRET in .env}
labels:
- "traefik.enable=true"
- "traefik.http.routers.edge-broker-api.rule=Host(`api.truckwash.dk`) && PathPrefix(`/edge-broker`)"
- "traefik.http.routers.edge-broker-api.entrypoints=websecure"
- "traefik.http.routers.edge-broker-api.tls=true"
- "traefik.http.routers.edge-broker-api.tls.certresolver=le"
- "traefik.http.routers.edge-broker-api.middlewares=secure-headers@file,edge-broker-strip"
- "traefik.http.routers.edge-broker-api.priority=200"
- "traefik.http.routers.edge-broker-api.service=edge-broker"
- "traefik.http.routers.edge-broker-api-io.rule=Host(`api.truckwash.io`) && PathPrefix(`/edge-broker`)"
- "traefik.http.routers.edge-broker-api-io.entrypoints=websecure"
- "traefik.http.routers.edge-broker-api-io.tls=true"
- "traefik.http.routers.edge-broker-api-io.tls.certresolver=le_io"
- "traefik.http.routers.edge-broker-api-io.middlewares=secure-headers@file,edge-broker-strip"
- "traefik.http.routers.edge-broker-api-io.priority=200"
- "traefik.http.routers.edge-broker-api-io.service=edge-broker"
- "traefik.http.routers.edge-broker-api-v2.rule=Host(`api-v2.truckwash.io`) && PathPrefix(`/edge-broker`)"
- "traefik.http.routers.edge-broker-api-v2.entrypoints=websecure"
- "traefik.http.routers.edge-broker-api-v2.tls=true"
- "traefik.http.routers.edge-broker-api-v2.tls.certresolver=le_io"
- "traefik.http.routers.edge-broker-api-v2.middlewares=secure-headers@file,edge-broker-strip"
- "traefik.http.routers.edge-broker-api-v2.priority=200"
- "traefik.http.routers.edge-broker-api-v2.service=edge-broker"
- "traefik.http.routers.edge-broker-api-staging.rule=Host(`api.truckwash.io`) && PathPrefix(`/edge-broker`)"
- "traefik.http.routers.edge-broker-api-staging.entrypoints=websecure-staging"
- "traefik.http.routers.edge-broker-api-staging.tls=true"
- "traefik.http.routers.edge-broker-api-staging.tls.certresolver=le_io"
- "traefik.http.routers.edge-broker-api-staging.middlewares=secure-headers@file,edge-broker-strip"
- "traefik.http.routers.edge-broker-api-staging.priority=200"
- "traefik.http.routers.edge-broker-api-staging.service=edge-broker"
- "traefik.http.routers.edge-broker-local.rule=Host(`localhost`) && PathPrefix(`/api/edge-broker`)"
- "traefik.http.routers.edge-broker-local.entrypoints=web"
- "traefik.http.routers.edge-broker-local.middlewares=secure-headers@file,edge-broker-strip-local"
- "traefik.http.routers.edge-broker-local.priority=200"
- "traefik.http.routers.edge-broker-local.service=edge-broker"
- "traefik.http.routers.edge-broker-local-secure.rule=Host(`localhost`) && PathPrefix(`/api/edge-broker`)"
- "traefik.http.routers.edge-broker-local-secure.entrypoints=websecure"
- "traefik.http.routers.edge-broker-local-secure.tls=true"
- "traefik.http.routers.edge-broker-local-secure.middlewares=secure-headers@file,edge-broker-strip-local"
- "traefik.http.routers.edge-broker-local-secure.priority=200"
- "traefik.http.routers.edge-broker-local-secure.service=edge-broker"
- "traefik.http.routers.edge-broker-local-staging.rule=Host(`localhost`) && PathPrefix(`/api/edge-broker`)"
- "traefik.http.routers.edge-broker-local-staging.entrypoints=websecure-staging"
- "traefik.http.routers.edge-broker-local-staging.tls=true"
- "traefik.http.routers.edge-broker-local-staging.middlewares=secure-headers@file,edge-broker-strip-local"
- "traefik.http.routers.edge-broker-local-staging.priority=200"
- "traefik.http.routers.edge-broker-local-staging.service=edge-broker"
- "traefik.http.middlewares.edge-broker-strip.stripPrefix.prefixes=/edge-broker"
- "traefik.http.middlewares.edge-broker-strip-local.stripPrefix.prefixes=/api/edge-broker"
- "traefik.http.services.edge-broker.loadbalancer.server.port=4300"
caddy:
image: caddy:2.7.6-alpine
container_name: caddy
depends_on:
- php1
- php2
- php3
- php4
- php5
volumes:
- ./services/nginx/app:/var/www/html
- ./services/caddy:/etc/caddy:ro
- ./services/caddy/logs:/var/log/caddy
labels:
- "traefik.enable=true"
# Public API (HTTPS via Traefik + LE)
- "traefik.http.routers.api.rule=Host(`api.truckwash.dk`)"
- "traefik.http.routers.api.entrypoints=websecure"
- "traefik.http.routers.api.tls=true"
- "traefik.http.routers.api.tls.domains[0].main=api.truckwash.dk"
- "traefik.http.routers.api.tls.certresolver=le"
- "traefik.http.routers.api.service=caddy"
- "traefik.http.routers.api.middlewares=secure-headers@file,api-ratelimit@file"
# Public API (.io version)
- "traefik.http.routers.api-io.rule=Host(`api.truckwash.io`)"
- "traefik.http.routers.api-io.entrypoints=websecure"
- "traefik.http.routers.api-io.tls=true"
- "traefik.http.routers.api-io.tls.domains[0].main=api.truckwash.io"
- "traefik.http.routers.api-io.tls.certresolver=le_io"
- "traefik.http.routers.api-io.service=caddy"
- "traefik.http.routers.api-io.middlewares=secure-headers@file,api-ratelimit@file"
# Public API (.io load-balanced gateway)
- "traefik.http.routers.api-v2.rule=Host(`api-v2.truckwash.io`)"
- "traefik.http.routers.api-v2.entrypoints=websecure"
- "traefik.http.routers.api-v2.tls=true"
- "traefik.http.routers.api-v2.tls.domains[0].main=api-v2.truckwash.io"
- "traefik.http.routers.api-v2.tls.certresolver=le_io"
- "traefik.http.routers.api-v2.service=caddy"
- "traefik.http.routers.api-v2.middlewares=secure-headers@file,api-ratelimit@file"
# HTTP to HTTPS redirect for both API domains
- "traefik.http.routers.api-http.rule=Host(`api.truckwash.dk`) || Host(`api.truckwash.io`) || Host(`api-v2.truckwash.io`)"
- "traefik.http.routers.api-http.entrypoints=web"
- "traefik.http.routers.api-http.middlewares=redirect-to-https@file"
- "traefik.http.routers.api-http.service=caddy"
# Local development (HTTP only)
- "traefik.http.routers.local.rule=Host(`localhost`)"
- "traefik.http.routers.local.entrypoints=web"
- "traefik.http.routers.local.service=caddy"
- "traefik.http.routers.local.middlewares=secure-headers@file"
# Local development over HTTPS (self-signed/default Traefik cert)
- "traefik.http.routers.local-secure.rule=Host(`localhost`)"
- "traefik.http.routers.local-secure.entrypoints=websecure"
- "traefik.http.routers.local-secure.tls=true"
- "traefik.http.routers.local-secure.service=caddy"
- "traefik.http.routers.local-secure.middlewares=secure-headers@file"
# Local alias: http://localhost/api -> Caddy (strip /api prefix)
- "traefik.http.routers.local-api.rule=Host(`localhost`) && PathPrefix(`/api`)"
- "traefik.http.routers.local-api.entrypoints=web"
- "traefik.http.routers.local-api.middlewares=strip-api-prefix@file,secure-headers@file"
- "traefik.http.routers.local-api.service=caddy"
- "traefik.http.routers.local-api.priority=100"
# Local alias over HTTPS as well: https://localhost/api -> Caddy (strip /api prefix)
- "traefik.http.routers.local-api-secure.rule=Host(`localhost`) && PathPrefix(`/api`)"
- "traefik.http.routers.local-api-secure.entrypoints=websecure"
- "traefik.http.routers.local-api-secure.tls=true"
- "traefik.http.routers.local-api-secure.middlewares=strip-api-prefix@file,secure-headers@file"
- "traefik.http.routers.local-api-secure.service=caddy"
- "traefik.http.routers.local-api-secure.priority=100"
# Tell Traefik which port Caddy listens on
- "traefik.http.services.caddy.loadbalancer.server.port=80"
caddy-staging:
image: caddy:2.7.6-alpine
container_name: caddy-staging
depends_on:
- php-staging
command: ["caddy", "run", "--config", "/etc/caddy/Caddyfile-staging", "--adapter", "caddyfile"]
volumes:
- ./services/nginx/staging:/var/www/html
- ./services/caddy:/etc/caddy:ro
- ./services/caddy/logs-staging:/var/log/caddy
labels:
- "traefik.enable=true"
# Staging API (.io version on port 4433)
- "traefik.http.routers.api-staging.rule=Host(`api.truckwash.io`)"
- "traefik.http.routers.api-staging.entrypoints=websecure-staging"
- "traefik.http.routers.api-staging.tls=true"
- "traefik.http.routers.api-staging.tls.domains[0].main=api.truckwash.io"
- "traefik.http.routers.api-staging.tls.certresolver=le_io"
- "traefik.http.routers.api-staging.service=caddy-staging"
- "traefik.http.routers.api-staging.middlewares=secure-headers@file,api-ratelimit@file"
# Local staging development (HTTP on port 4433)
- "traefik.http.routers.local-staging.rule=Host(`localhost`)"
- "traefik.http.routers.local-staging.entrypoints=websecure-staging"
- "traefik.http.routers.local-staging.service=caddy-staging"
- "traefik.http.routers.local-staging.middlewares=secure-headers@file"
# Local staging development (HTTPS on port 4433)
- "traefik.http.routers.local-staging-secure.rule=Host(`localhost`)"
- "traefik.http.routers.local-staging-secure.entrypoints=websecure-staging"
- "traefik.http.routers.local-staging-secure.tls=true"
- "traefik.http.routers.local-staging-secure.service=caddy-staging"
- "traefik.http.routers.local-staging-secure.middlewares=secure-headers@file"
# Local staging alias: http://localhost:4433/api -> Caddy staging (strip /api prefix)
- "traefik.http.routers.local-staging-api.rule=Host(`localhost`) && PathPrefix(`/api`)"
- "traefik.http.routers.local-staging-api.entrypoints=websecure-staging"
- "traefik.http.routers.local-staging-api.middlewares=strip-api-prefix@file,secure-headers@file"
- "traefik.http.routers.local-staging-api.service=caddy-staging"
- "traefik.http.routers.local-staging-api.priority=100"
# Local staging alias over HTTPS: https://localhost:4433/api -> Caddy staging (strip /api prefix)
- "traefik.http.routers.local-staging-api-secure.rule=Host(`localhost`) && PathPrefix(`/api`)"
- "traefik.http.routers.local-staging-api-secure.entrypoints=websecure-staging"
- "traefik.http.routers.local-staging-api-secure.tls=true"
- "traefik.http.routers.local-staging-api-secure.middlewares=strip-api-prefix@file,secure-headers@file"
- "traefik.http.routers.local-staging-api-secure.service=caddy-staging"
- "traefik.http.routers.local-staging-api-secure.priority=100"
# Tell Traefik which port Caddy listens on
- "traefik.http.services.caddy-staging.loadbalancer.server.port=80"
php1:
build:
context: .
dockerfile: services/php/Dockerfile
container_name: php1
depends_on:
- redis
- edge-broker
command: ["php-fpm"]
env_file:
- .env
environment:
AUTO_COMPOSER_INSTALL: "true"
EDGE_BROKER_URL: ${EDGE_BROKER_URL:-http://edge-broker:4300}
EDGE_BROKER_SHARED_SECRET: ${EDGE_BROKER_SHARED_SECRET:?set EDGE_BROKER_SHARED_SECRET in .env}
volumes:
- ./services/nginx/app:/var/www/html
- ./services/php/php.ini:/usr/local/etc/php/conf.d/zz-custom.ini:ro
- ./services/php/logs:/var/log/php
php2:
build:
context: .
dockerfile: services/php/Dockerfile
container_name: php2
depends_on:
- redis
- edge-broker
command: ["php-fpm"]
env_file:
- .env
environment:
AUTO_COMPOSER_INSTALL: "false"
EDGE_BROKER_URL: ${EDGE_BROKER_URL:-http://edge-broker:4300}
EDGE_BROKER_SHARED_SECRET: ${EDGE_BROKER_SHARED_SECRET:?set EDGE_BROKER_SHARED_SECRET in .env}
volumes:
- ./services/nginx/app:/var/www/html
- ./services/php/php.ini:/usr/local/etc/php/conf.d/zz-custom.ini:ro
- ./services/php/logs:/var/log/php
php3:
build:
context: .
dockerfile: services/php/Dockerfile
container_name: php3
depends_on:
- redis
- edge-broker
command: ["php-fpm"]
env_file:
- .env
environment:
AUTO_COMPOSER_INSTALL: "false"
EDGE_BROKER_URL: ${EDGE_BROKER_URL:-http://edge-broker:4300}
EDGE_BROKER_SHARED_SECRET: ${EDGE_BROKER_SHARED_SECRET:?set EDGE_BROKER_SHARED_SECRET in .env}
volumes:
- ./services/nginx/app:/var/www/html
- ./services/php/php.ini:/usr/local/etc/php/conf.d/zz-custom.ini:ro
- ./services/php/logs:/var/log/php
php4:
build:
context: .
dockerfile: services/php/Dockerfile
container_name: php4
depends_on:
- redis
- edge-broker
command: ["php-fpm"]
env_file:
- .env
environment:
AUTO_COMPOSER_INSTALL: "false"
EDGE_BROKER_URL: ${EDGE_BROKER_URL:-http://edge-broker:4300}
EDGE_BROKER_SHARED_SECRET: ${EDGE_BROKER_SHARED_SECRET:?set EDGE_BROKER_SHARED_SECRET in .env}
volumes:
- ./services/nginx/app:/var/www/html
- ./services/php/php.ini:/usr/local/etc/php/conf.d/zz-custom.ini:ro
- ./services/php/logs:/var/log/php
php5:
build:
context: .
dockerfile: services/php/Dockerfile
container_name: php5
depends_on:
- redis
- edge-broker
command: ["php-fpm"]
env_file:
- .env
environment:
AUTO_COMPOSER_INSTALL: "false"
EDGE_BROKER_URL: ${EDGE_BROKER_URL:-http://edge-broker:4300}
EDGE_BROKER_SHARED_SECRET: ${EDGE_BROKER_SHARED_SECRET:?set EDGE_BROKER_SHARED_SECRET in .env}
volumes:
- ./services/nginx/app:/var/www/html
- ./services/php/php.ini:/usr/local/etc/php/conf.d/zz-custom.ini:ro
- ./services/php/logs:/var/log/php
php-staging:
build:
context: .
dockerfile: services/php/Dockerfile
container_name: php-staging
depends_on:
- redis-staging
- edge-broker
command: ["php-fpm"]
env_file:
- .env.staging
environment:
AUTO_COMPOSER_INSTALL: "false"
EDGE_BROKER_URL: ${EDGE_BROKER_URL:-http://edge-broker:4300}
EDGE_BROKER_SHARED_SECRET: ${EDGE_BROKER_SHARED_SECRET:?set EDGE_BROKER_SHARED_SECRET in .env}
volumes:
- ./services/nginx/staging:/var/www/html
- ./services/php/php.ini:/usr/local/etc/php/conf.d/zz-custom.ini:ro
- ./services/php/logs-staging:/var/log/php
php-cron:
build:
context: .
dockerfile: services/php/Dockerfile
container_name: php-cron
depends_on:
- redis
- edge-broker
command: ["php", "index.php", "run", "cron-worker"]
env_file:
- .env
environment:
AUTO_COMPOSER_INSTALL: "false"
EDGE_BROKER_URL: ${EDGE_BROKER_URL:-http://edge-broker:4300}
EDGE_BROKER_SHARED_SECRET: ${EDGE_BROKER_SHARED_SECRET:?set EDGE_BROKER_SHARED_SECRET in .env}
volumes:
- ./services/nginx/app:/var/www/html
- ./services/php/php.ini:/usr/local/etc/php/conf.d/zz-custom.ini:ro
- ./services/php/logs:/var/log/php
portainer:
image: portainer/portainer-ce:2.21.4
container_name: portainer
profiles:
- dev
ports:
- "9443:9443"
- "9000:9000"
volumes:
- /var/run/docker.sock:/var/run/docker.sock
- portainer_data:/data
# Jaeger all-in-one for local tracing (Traefik → Jaeger)
jaeger:
image: jaegertracing/all-in-one:1.53
container_name: jaeger
profiles:
- dev
environment:
- COLLECTOR_ZIPKIN_HTTP_PORT=9411
ports:
- "16686:16686" # Jaeger UI
# No volumes needed for dev; data is ephemeral
n8n:
image: n8nio/n8n:latest
container_name: n8n
restart: always
environment:
- N8N_HOST=n8n.truckwash.io
- N8N_PORT=5678
- N8N_PROTOCOL=https
- NODE_ENV=production
- WEBHOOK_URL=https://n8n.truckwash.io/
- GENERIC_TIMEZONE=${CONFIG_TIMEZONE:-Europe/Copenhagen}
volumes:
- n8n_data:/home/node/.n8n
labels:
- "traefik.enable=true"
# n8n over HTTPS (le_io cert resolver)
- "traefik.http.routers.n8n.rule=Host(`n8n.truckwash.io`)"
- "traefik.http.routers.n8n.entrypoints=websecure"
- "traefik.http.routers.n8n.tls=true"
- "traefik.http.routers.n8n.tls.certresolver=le_io"
- "traefik.http.routers.n8n.service=n8n"
# n8n HTTP to HTTPS redirect
- "traefik.http.routers.n8n-http.rule=Host(`n8n.truckwash.io`)"
- "traefik.http.routers.n8n-http.entrypoints=web"
- "traefik.http.routers.n8n-http.middlewares=redirect-to-https@file"
- "traefik.http.routers.n8n-http.service=n8n"
# n8n service port
- "traefik.http.services.n8n.loadbalancer.server.port=5678"
volumes:
db_data:
db_debug_data:
nnks_redis:
nnks_redis_staging:
es_data:
portainer_data:
fleet-server-data:
elastic-agent-data:
n8n_data:
+4
View File
@@ -0,0 +1,4 @@
# AGENT MCP SMOKE
Generated 20260813-091957 by hermes agent to verify GitHub MCP wiring.
Safe to close.
+115
View File
@@ -0,0 +1,115 @@
# Plan: Remove broken Coolify cron-worker deployment; add reliable 5-min cron
## Audit findings
The "Coolify cron worker flow" is a **dual-deployment mechanism** that:
- Tries to auto-deploy a **separate Coolify "cron" application** every time the API is deployed
- That separate app runs `php index.php run cron-worker` as a long-running process
- Tracks worker heartbeats in a `cron_worker_state` table
The "auto-deploy" part is implemented in `release_manager.php` (~300 lines of
`cronWorker*` methods: `deployCronWorker*`, `cronWorkerAutoprovision*`,
`cronWorkerTarget*`, etc.) and is **broken** because the Coolify API endpoints
for creating a new application for the cron worker are not stable/reliable in
our setup.
Meanwhile, the **actual cron mechanism** (`cron_worker.php`, `cron_scheduler.php`,
`cron_task_registry.php`, and the 20+ scheduled tasks in `modules/*/cron/tasks.php`)
is sound. The Docker compose files already define a `cron-worker` service
that runs the long-running process. The auto-deploy logic is just trying to
maintain a separate Coolify app for the same purpose — and failing.
## The plan
### 1. Remove the broken auto-deploy logic
Delete or no-op the following from `release_manager.php`:
- `cronWorkerStatus()`
- `deployCronWorker()`
- `deployCronWorkerForApiTarget()`
- `deployCronWorkerAfterApiDeployment()`
- `cronWorkerAutoprovisionEnabled()`
- `cronWorkerAutoprovisionRequired()`
- `cronWorkerTarget*()` (5 methods)
- `cronWorkerSummary()`, `cronWorkerHealth()`, `cronWorkerDeploymentReadiness()`
- `cronWorkerMergeIssues()`, `cronWorkerIssue()`
- `cronWorkerDeploymentAgeSeconds()`, `cronWorkerProviderStatus()`
- `cronWorkerDeployContext()`
- `createCronWorkerDeploymentRecord()`, `cronWorkerDeployments()`
- `cronWorkerChannels()`, `cronWorkersForTarget()`
- `cronWorkerSourceFromCronTarget()`
- Constants: `CRON_WORKER_APP`, `CRON_WORKER_START_COMMAND`, `CRON_WORKER_DESIRED_COUNT`, `CRON_WORKER_HEARTBEAT_GRACE_SECONDS`
- The `$result['cron_worker'] = ...` call after API deployment
Keep:
- `cron_worker.php` class (the actual worker)
- `cron_scheduler.php`, `cron_schedule.php`, `cron_task_registry.php`
- `cron_schema_bootstrap.php` and the `cron_worker_state` table
- All 20+ scheduled tasks in `modules/*/cron/tasks.php`
- The `cron-worker` service in `docker-compose*.yml`
- The `cron-worker` case in `cli.php`
### 2. Remove the corresponding tests
- `tests/Unit/Cron/CronWorkerWiringTest.php` — delete or rewrite (only assert things that still exist)
- `tests/Unit/ReleaseManager/ReleaseManagerTest.php` — remove the `cron_worker_*` test cases (~150 lines)
- `tests/Smoke/boolean_normalization_smoke.php` — remove cron_worker reference
### 3. Add a reliable 5-min cron mechanism
Two-layer approach:
1. **Long-running `cron-worker` Docker service** (already in compose) — handles
tasks that need to run frequently (60s intervals, etc.). Started automatically
with the rest of the stack.
2. **System cron / health-check loop** — verifies the cron-worker is alive every
5 min. If no fresh heartbeat in 10 min, alert.
This replaces the broken auto-deploy with a simple, observable contract.
### 4. Add a verification harness
`/workspace/scripts/verify-api-cron.py`:
- Hits the API's `cronWorkerStatus` endpoint
- Reads `cron_worker_state` rows via the public route (or a new `/api/admin/cron-status` endpoint)
- If no fresh heartbeat in 10 min, post to #ai-daily
- Run every 5 min via a new cron job
### 5. Update documentation
- `inventory/self-serve-inventory.md` — remove coolify-cron-worker references
- `openapi.yaml` — remove `cron_worker_status` route documentation
- `routes/cronRoute.php` — remove the coolify-cron-worker endpoints
## Acceptance criteria
- [ ] `release_manager.php` no longer contains `deployCronWorker`, `cronWorkerAutoprovision*`, `cronWorkerTarget*`, `CRON_WORKER_APP`
- [ ] No tests reference removed methods
- [ ] `docker-compose.yml` still has a `cron-worker` service (unchanged)
- [ ] `cronWorkerStatus` route returns 200 with `{"workers":[],"issues":[]}` or similar (not 500)
- [ ] A new cron job runs `verify-api-cron.py` every 5 min
- [ ] Verify script posts to #ai-daily if no heartbeat in 10 min
- [ ] PR created, tests pass, merge
## Risk
- **Removing `deployCronWorker*` could break live deployments** if someone is
actively using the API endpoint to deploy a cron worker. Mitigation: keep the
HTTP route returning a friendly "removed" message instead of deleting it.
- **Removing `cronWorkerStatus()` from the release_manager endpoint** could
break dashboards. Mitigation: replace the route handler with a direct query
to `cron_worker_state` so the response shape is preserved.
## Steps
1. Create a feature branch `fix/remove-coolify-cron-worker`
2. Edit `release_manager.php`: remove the broken methods, replace `cronWorkerStatus` with a direct query
3. Edit `tests/Unit/ReleaseManager/ReleaseManagerTest.php`: remove cron_worker tests
4. Edit `tests/Unit/Cron/CronWorkerWiringTest.php`: drop assertions on removed wiring
5. Edit `routes/cronRoute.php`: keep the status endpoint but call the new direct query
6. Edit `cli.php`: no change needed (cron-worker case still works)
7. Edit `docker-compose*.yml`: no change needed (cron-worker service unchanged)
8. Create `/workspace/scripts/verify-api-cron.py` for the verification harness
9. Add a new cron job `5 * * * *` Europe/Copenhagen that runs `verify-api-cron.py`
10. Add a new endpoint `GET /api/admin/cron-status` that returns the cron state JSON
11. Run the test suite locally
12. Push branch, create PR, get user review
+76
View File
@@ -0,0 +1,76 @@
# Copenhagen Truck Wash API — Writerside Style Guide
# This file defines the style rules for our documentation.
# For more details, see https://vale.sh/docs/topics/styles/#extension-points
# --- RULE: Encourage descriptive language (existence) ---
extends: existence
message: "Avoid using '%s'. Try to be more descriptive or direct."
level: warning
ignorecase: true
tokens:
- simply
- just
- easy
- easily
- simple
- basically
- obviously
- actually
- very
- really
- pretty
- quite
- rather
---
# --- RULE: Flag incomplete documentation (existence) ---
extends: existence
message: "Incomplete documentation: '%s' found. Please provide comprehensive details."
level: error
ignorecase: true
tokens:
- TBD
- TO BE DETERMINED
- TODO
- FIXME
- Placeholder
- Coming soon
---
# --- RULE: Preferred terminology (substitution) ---
extends: substitution
message: "Consider using '%s' instead of its informal or less descriptive counterpart."
level: suggestion
ignorecase: true
swap:
check[ -]box: checkbox
right-click menu: context menu|popup menu
webpage: page
click on: click|select
press: press|select
setup: set up
log[ -]in: sign in|log in
e-mail: email
interface: UI|interface
the following: :
utilize: use
functionality: feature|function
additional: more|extra
---
# --- RULE: Avoid jargon and filler (existence) ---
extends: existence
message: "Avoid jargon or filler phrases like '%s'."
level: warning
ignorecase: true
tokens:
- leverage
- bandwidth
- synergy
- best-in-class
- cutting-edge
- robust
- state-of-the-art
- mission-critical
- go-forward
- paradigm shift
Binary file not shown.
+493
View File
@@ -0,0 +1,493 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Build results</title>
</head>
<body>
<h1>Tests</h1>
<p>180 tests total.</p>
<ul>
<li><a href="#errors">3 errors</a></li>
<li><a href="#warnings">1 warnings</a></li>
<li><a href="#passed">176 passed</a></li>
</ul>
<h2 id="errors">Errors</h2>
<ul style="list-style: none;">
<li><a href="#MRK004"><code>MRK004</code> — Topic ID doesn't match the containing file name</a></li>
<li><a href="#REF006"><code>REF006</code> — 'toc-element' for the current instance references a topic file that cannot be found</a></li>
<li><a href="#TOC001"><code>TOC001</code>&lt;toc-element&gt; points to an article that doesn't exist</a></li>
</ul>
<h3 id="MRK004"><code>MRK004</code> — Topic ID doesn't match the containing file name</h3>
<ul id="MRK004-details">
<li id="MRK004-In-Config_Module_Backups-topic-4-1">In Config_Module_Backups.topic:4:1</li>
<li id="MRK004-In-Config_Module_Backups_Page_1-topic-4-1">In Config_Module_Backups_Page_1.topic:4:1</li>
<li id="MRK004-In-Config_Module_Bird-topic-4-1">In Config_Module_Bird.topic:4:1</li>
<li id="MRK004-In-Config_Module_Bird_Page_1-topic-4-1">In Config_Module_Bird_Page_1.topic:4:1</li>
<li id="MRK004-In-Config_Module_Email-topic-4-1">In Config_Module_Email.topic:4:1</li>
<li id="MRK004-In-Config_Module_Email_Page_1-topic-4-1">In Config_Module_Email_Page_1.topic:4:1</li>
<li id="MRK004-In-Config_Module_Entra-topic-4-1">In Config_Module_Entra.topic:4:1</li>
<li id="MRK004-In-Config_Module_Entra_Page_1-topic-4-1">In Config_Module_Entra_Page_1.topic:4:1</li>
<li id="MRK004-In-Config_Module_FXRatesAPI-topic-4-1">In Config_Module_FXRatesAPI.topic:4:1</li>
<li id="MRK004-In-Config_Module_FXRatesAPI_Page_1-topic-4-1">In Config_Module_FXRatesAPI_Page_1.topic:4:1</li>
<li id="MRK004-In-Config_Module_GatewayAPI-topic-4-1">In Config_Module_GatewayAPI.topic:4:1</li>
<li id="MRK004-In-Config_Module_GatewayAPI_Page_1-topic-4-1">In Config_Module_GatewayAPI_Page_1.topic:4:1</li>
<li id="MRK004-In-Config_Module_LicensePlateRecognizer-topic-4-1">In Config_Module_LicensePlateRecognizer.topic:4:1</li>
<li id="MRK004-In-Config_Module_LicensePlateRecognizer_Page_1-topic-4-1">In Config_Module_LicensePlateRecognizer_Page_1.topic:4:1</li>
<li id="MRK004-In-Config_Module_Limble-topic-4-1">In Config_Module_Limble.topic:4:1</li>
<li id="MRK004-In-Config_Module_Limble_Page_1-topic-4-1">In Config_Module_Limble_Page_1.topic:4:1</li>
<li id="MRK004-In-Config_Module_MotorAPI-topic-4-1">In Config_Module_MotorAPI.topic:4:1</li>
<li id="MRK004-In-Config_Module_MotorAPI_Page_1-topic-4-1">In Config_Module_MotorAPI_Page_1.topic:4:1</li>
<li id="MRK004-In-Config_Module_OcrSpace-topic-4-1">In Config_Module_OcrSpace.topic:4:1</li>
<li id="MRK004-In-Config_Module_OcrSpace_Page_1-topic-4-1">In Config_Module_OcrSpace_Page_1.topic:4:1</li>
<li id="MRK004-In-Config_Module_OpenAI-topic-4-1">In Config_Module_OpenAI.topic:4:1</li>
<li id="MRK004-In-Config_Module_OpenAI_Page_1-topic-4-1">In Config_Module_OpenAI_Page_1.topic:4:1</li>
<li id="MRK004-In-Config_Module_Self_Serve-topic-4-1">In Config_Module_Self_Serve.topic:4:1</li>
<li id="MRK004-In-Config_Module_Self_Serve_Page_1-topic-4-1">In Config_Module_Self_Serve_Page_1.topic:4:1</li>
<li id="MRK004-In-Config_Module_Shelly-topic-4-1">In Config_Module_Shelly.topic:4:1</li>
<li id="MRK004-In-Config_Module_Shelly_Page_1-topic-4-1">In Config_Module_Shelly_Page_1.topic:4:1</li>
<li id="MRK004-In-Config_Module_Stripe-topic-4-1">In Config_Module_Stripe.topic:4:1</li>
<li id="MRK004-In-Config_Module_Stripe_Page_1-topic-4-1">In Config_Module_Stripe_Page_1.topic:4:1</li>
<li id="MRK004-In-Config_Module_VirkData-topic-4-1">In Config_Module_VirkData.topic:4:1</li>
<li id="MRK004-In-Config_Module_VirkData_Page_1-topic-4-1">In Config_Module_VirkData_Page_1.topic:4:1</li>
<li id="MRK004-In-Config_Module_WeatherAPI-topic-4-1">In Config_Module_WeatherAPI.topic:4:1</li>
<li id="MRK004-In-Config_Module_WeatherAPI_Page_1-topic-4-1">In Config_Module_WeatherAPI_Page_1.topic:4:1</li>
<li id="MRK004-In-Config_Module_XLVask-topic-4-1">In Config_Module_XLVask.topic:4:1</li>
<li id="MRK004-In-Config_Module_XLVask_Page_1-topic-4-1">In Config_Module_XLVask_Page_1.topic:4:1</li>
<li id="MRK004-In-Config_Module_e_conomic-topic-4-1">In Config_Module_e_conomic.topic:4:1</li>
<li id="MRK004-In-Config_Module_e_conomic_Page_1-topic-4-1">In Config_Module_e_conomic_Page_1.topic:4:1</li>
<li id="MRK004-In-Config_Module_reCAPTCHA-topic-4-1">In Config_Module_reCAPTCHA.topic:4:1</li>
<li id="MRK004-In-Config_Module_reCAPTCHA_Page_1-topic-4-1">In Config_Module_reCAPTCHA_Page_1.topic:4:1</li>
<li id="MRK004-In-Modules_Module_Action_Logs-topic-4-1">In Modules_Module_Action_Logs.topic:4:1</li>
<li id="MRK004-In-Modules_Module_Action_Logs_Page_1-topic-4-1">In Modules_Module_Action_Logs_Page_1.topic:4:1</li>
<li id="MRK004-In-Modules_Module_Backup-topic-4-1">In Modules_Module_Backup.topic:4:1</li>
<li id="MRK004-In-Modules_Module_Backup_Page_1-topic-4-1">In Modules_Module_Backup_Page_1.topic:4:1</li>
<li id="MRK004-In-Modules_Module_CVR-topic-4-1">In Modules_Module_CVR.topic:4:1</li>
<li id="MRK004-In-Modules_Module_CVR_Page_1-topic-4-1">In Modules_Module_CVR_Page_1.topic:4:1</li>
<li id="MRK004-In-Modules_Module_Entra-topic-4-1">In Modules_Module_Entra.topic:4:1</li>
<li id="MRK004-In-Modules_Module_Entra_Page_1-topic-4-1">In Modules_Module_Entra_Page_1.topic:4:1</li>
<li id="MRK004-In-Modules_Module_FXRatesAPI-topic-4-1">In Modules_Module_FXRatesAPI.topic:4:1</li>
<li id="MRK004-In-Modules_Module_FXRatesAPI_Page_1-topic-4-1">In Modules_Module_FXRatesAPI_Page_1.topic:4:1</li>
<li id="MRK004-In-Modules_Module_MotorAPI-topic-4-1">In Modules_Module_MotorAPI.topic:4:1</li>
<li id="MRK004-In-Modules_Module_MotorAPI_Page_1-topic-4-1">In Modules_Module_MotorAPI_Page_1.topic:4:1</li>
<li id="MRK004-In-Modules_Module_Self_Serve-topic-4-1">In Modules_Module_Self_Serve.topic:4:1</li>
<li id="MRK004-In-Modules_Module_Self_Serve_Page_1-topic-4-1">In Modules_Module_Self_Serve_Page_1.topic:4:1</li>
<li id="MRK004-In-Modules_Module_Stripe-topic-4-1">In Modules_Module_Stripe.topic:4:1</li>
<li id="MRK004-In-Modules_Module_Stripe_Page_1-topic-4-1">In Modules_Module_Stripe_Page_1.topic:4:1</li>
<li id="MRK004-In-Modules_Module_VirkData-topic-4-1">In Modules_Module_VirkData.topic:4:1</li>
<li id="MRK004-In-Modules_Module_VirkData_Page_1-topic-4-1">In Modules_Module_VirkData_Page_1.topic:4:1</li>
<li id="MRK004-In-Modules_Module_Wash_Certificates-topic-4-1">In Modules_Module_Wash_Certificates.topic:4:1</li>
<li id="MRK004-In-Modules_Module_Wash_Certificates_Page_1-topic-4-1">In Modules_Module_Wash_Certificates_Page_1.topic:4:1</li>
<li id="MRK004-In-Modules_Module_WeatherAPI-topic-4-1">In Modules_Module_WeatherAPI.topic:4:1</li>
<li id="MRK004-In-Modules_Module_WeatherAPI_Page_1-topic-4-1">In Modules_Module_WeatherAPI_Page_1.topic:4:1</li>
<li id="MRK004-In-Modules_Module_XLVask-topic-4-1">In Modules_Module_XLVask.topic:4:1</li>
<li id="MRK004-In-Modules_Module_XLVask_Page_1-topic-4-1">In Modules_Module_XLVask_Page_1.topic:4:1</li>
<li id="MRK004-In-Modules_Module_e_conomic-topic-4-1">In Modules_Module_e_conomic.topic:4:1</li>
<li id="MRK004-In-Modules_Module_e_conomic_Page_1-topic-4-1">In Modules_Module_e_conomic_Page_1.topic:4:1</li>
</ul>
<h3 id="REF006"><code>REF006</code> — 'toc-element' for the current instance references a topic file that cannot be found</h3>
<ul id="REF006-details">
<li id="REF006---API_Reference-topic---in-ctw-tree-613-5">&quot;&quot;API_Reference.topic&quot;&quot; in ctw.tree:613:5</li>
<li id="REF006---Admin_Bookings_CompleteWithoutWashCertificate_POST-topic---in-ctw-tree-629-5">&quot;&quot;Admin_Bookings_CompleteWithoutWashCertificate_POST.topic&quot;&quot; in ctw.tree:629:5</li>
<li id="REF006---Admin_Bookings_Delete_POST-topic---in-ctw-tree-626-5">&quot;&quot;Admin_Bookings_Delete_POST.topic&quot;&quot; in ctw.tree:626:5</li>
<li id="REF006---Admin_Bookings_Department_Count_GET-topic---in-ctw-tree-632-5">&quot;&quot;Admin_Bookings_Department_Count_GET.topic&quot;&quot; in ctw.tree:632:5</li>
<li id="REF006---Admin_Bookings_Sync_POST-topic---in-ctw-tree-624-5">&quot;&quot;Admin_Bookings_Sync_POST.topic&quot;&quot; in ctw.tree:624:5</li>
<li id="REF006---Bookings_Download_PDF_GET-topic---in-ctw-tree-621-5">&quot;&quot;Bookings_Download_PDF_GET.topic&quot;&quot; in ctw.tree:621:5</li>
<li id="REF006---Bookings_GET-topic---in-ctw-tree-633-5">&quot;&quot;Bookings_GET.topic&quot;&quot; in ctw.tree:633:5</li>
<li id="REF006---Bookings_PUT-topic---in-ctw-tree-628-5">&quot;&quot;Bookings_PUT.topic&quot;&quot; in ctw.tree:628:5</li>
<li id="REF006---Customers_GET-topic---in-ctw-tree-618-5">&quot;&quot;Customers_GET.topic&quot;&quot; in ctw.tree:618:5</li>
<li id="REF006---Customers_POST-topic---in-ctw-tree-615-5">&quot;&quot;Customers_POST.topic&quot;&quot; in ctw.tree:615:5</li>
<li id="REF006---Customers_PUT-topic---in-ctw-tree-608-5">&quot;&quot;Customers_PUT.topic&quot;&quot; in ctw.tree:608:5</li>
<li id="REF006---Customers_id_DELETE-topic---in-ctw-tree-610-5">&quot;&quot;Customers_id_DELETE.topic&quot;&quot; in ctw.tree:610:5</li>
<li id="REF006---Department_Timebookings_Entries_Public_GET-topic---in-ctw-tree-625-5">&quot;&quot;Department_Timebookings_Entries_Public_GET.topic&quot;&quot; in ctw.tree:625:5</li>
<li id="REF006---Department_Timebookings_Entries_Public_POST-topic---in-ctw-tree-620-5">&quot;&quot;Department_Timebookings_Entries_Public_POST.topic&quot;&quot; in ctw.tree:620:5</li>
<li id="REF006---Department_Timebookings_OpeningHours_Public_GET-topic---in-ctw-tree-630-5">&quot;&quot;Department_Timebookings_OpeningHours_Public_GET.topic&quot;&quot; in ctw.tree:630:5</li>
<li id="REF006---Department_Timebookings_Types_Public_GET-topic---in-ctw-tree-627-5">&quot;&quot;Department_Timebookings_Types_Public_GET.topic&quot;&quot; in ctw.tree:627:5</li>
<li id="REF006---Superuser_Bookings_Sync_All_POST-topic---in-ctw-tree-631-5">&quot;&quot;Superuser_Bookings_Sync_All_POST.topic&quot;&quot; in ctw.tree:631:5</li>
<li id="REF006---UsageLog_GET-topic---in-ctw-tree-617-5">&quot;&quot;UsageLog_GET.topic&quot;&quot; in ctw.tree:617:5</li>
<li id="REF006---User_Bookings_Delete_POST-topic---in-ctw-tree-619-5">&quot;&quot;User_Bookings_Delete_POST.topic&quot;&quot; in ctw.tree:619:5</li>
<li id="REF006---User_Bookings_GET-topic---in-ctw-tree-623-5">&quot;&quot;User_Bookings_GET.topic&quot;&quot; in ctw.tree:623:5</li>
<li id="REF006---User_Bookings_Washcertificate_Download_POST-topic---in-ctw-tree-622-5">&quot;&quot;User_Bookings_Washcertificate_Download_POST.topic&quot;&quot; in ctw.tree:622:5</li>
<li id="REF006---Vehicles_GET-topic---in-ctw-tree-609-5">&quot;&quot;Vehicles_GET.topic&quot;&quot; in ctw.tree:609:5</li>
<li id="REF006---Vehicles_POST-topic---in-ctw-tree-616-5">&quot;&quot;Vehicles_POST.topic&quot;&quot; in ctw.tree:616:5</li>
<li id="REF006---Vehicles_PUT-topic---in-ctw-tree-607-5">&quot;&quot;Vehicles_PUT.topic&quot;&quot; in ctw.tree:607:5</li>
<li id="REF006---Vehicles_id_DELETE-topic---in-ctw-tree-614-5">&quot;&quot;Vehicles_id_DELETE.topic&quot;&quot; in ctw.tree:614:5</li>
<li id="REF006---config_module_backups-topic---in-ctw-tree-394-13">&quot;&quot;config_module_backups.topic&quot;&quot; in ctw.tree:394:13</li>
<li id="REF006---config_module_backups_page_1-topic---in-ctw-tree-395-17">&quot;&quot;config_module_backups_page_1.topic&quot;&quot; in ctw.tree:395:17</li>
<li id="REF006---config_module_bird-topic---in-ctw-tree-400-13">&quot;&quot;config_module_bird.topic&quot;&quot; in ctw.tree:400:13</li>
<li id="REF006---config_module_bird_page_1-topic---in-ctw-tree-401-17">&quot;&quot;config_module_bird_page_1.topic&quot;&quot; in ctw.tree:401:17</li>
<li id="REF006---config_module_e_conomic-topic---in-ctw-tree-406-13">&quot;&quot;config_module_e_conomic.topic&quot;&quot; in ctw.tree:406:13</li>
<li id="REF006---config_module_e_conomic_page_1-topic---in-ctw-tree-407-17">&quot;&quot;config_module_e_conomic_page_1.topic&quot;&quot; in ctw.tree:407:17</li>
<li id="REF006---config_module_email-topic---in-ctw-tree-412-13">&quot;&quot;config_module_email.topic&quot;&quot; in ctw.tree:412:13</li>
<li id="REF006---config_module_email_page_1-topic---in-ctw-tree-413-17">&quot;&quot;config_module_email_page_1.topic&quot;&quot; in ctw.tree:413:17</li>
<li id="REF006---config_module_entra-topic---in-ctw-tree-419-13">&quot;&quot;config_module_entra.topic&quot;&quot; in ctw.tree:419:13</li>
<li id="REF006---config_module_entra_page_1-topic---in-ctw-tree-420-17">&quot;&quot;config_module_entra_page_1.topic&quot;&quot; in ctw.tree:420:17</li>
<li id="REF006---config_module_fxratesapi-topic---in-ctw-tree-425-13">&quot;&quot;config_module_fxratesapi.topic&quot;&quot; in ctw.tree:425:13</li>
<li id="REF006---config_module_fxratesapi_page_1-topic---in-ctw-tree-426-17">&quot;&quot;config_module_fxratesapi_page_1.topic&quot;&quot; in ctw.tree:426:17</li>
<li id="REF006---config_module_gatewayapi-topic---in-ctw-tree-431-13">&quot;&quot;config_module_gatewayapi.topic&quot;&quot; in ctw.tree:431:13</li>
<li id="REF006---config_module_gatewayapi_page_1-topic---in-ctw-tree-432-17">&quot;&quot;config_module_gatewayapi_page_1.topic&quot;&quot; in ctw.tree:432:17</li>
<li id="REF006---config_module_licenseplaterecognizer-topic---in-ctw-tree-437-13">&quot;&quot;config_module_licenseplaterecognizer.topic&quot;&quot; in ctw.tree:437:13</li>
<li id="REF006---config_module_licenseplaterecognizer_page_1-topic---in-ctw-tree-438-17">&quot;&quot;config_module_licenseplaterecognizer_page_1.topic&quot;&quot; in ctw.tree:438:17</li>
<li id="REF006---config_module_limble-topic---in-ctw-tree-443-13">&quot;&quot;config_module_limble.topic&quot;&quot; in ctw.tree:443:13</li>
<li id="REF006---config_module_limble_page_1-topic---in-ctw-tree-444-17">&quot;&quot;config_module_limble_page_1.topic&quot;&quot; in ctw.tree:444:17</li>
<li id="REF006---config_module_motorapi-topic---in-ctw-tree-449-13">&quot;&quot;config_module_motorapi.topic&quot;&quot; in ctw.tree:449:13</li>
<li id="REF006---config_module_motorapi_page_1-topic---in-ctw-tree-450-17">&quot;&quot;config_module_motorapi_page_1.topic&quot;&quot; in ctw.tree:450:17</li>
<li id="REF006---config_module_ocrspace-topic---in-ctw-tree-455-13">&quot;&quot;config_module_ocrspace.topic&quot;&quot; in ctw.tree:455:13</li>
<li id="REF006---config_module_ocrspace_page_1-topic---in-ctw-tree-456-17">&quot;&quot;config_module_ocrspace_page_1.topic&quot;&quot; in ctw.tree:456:17</li>
<li id="REF006---config_module_openai-topic---in-ctw-tree-461-13">&quot;&quot;config_module_openai.topic&quot;&quot; in ctw.tree:461:13</li>
<li id="REF006---config_module_openai_page_1-topic---in-ctw-tree-462-17">&quot;&quot;config_module_openai_page_1.topic&quot;&quot; in ctw.tree:462:17</li>
<li id="REF006---config_module_recaptcha-topic---in-ctw-tree-467-13">&quot;&quot;config_module_recaptcha.topic&quot;&quot; in ctw.tree:467:13</li>
<li id="REF006---config_module_recaptcha_page_1-topic---in-ctw-tree-468-17">&quot;&quot;config_module_recaptcha_page_1.topic&quot;&quot; in ctw.tree:468:17</li>
<li id="REF006---config_module_self_serve-topic---in-ctw-tree-473-13">&quot;&quot;config_module_self_serve.topic&quot;&quot; in ctw.tree:473:13</li>
<li id="REF006---config_module_self_serve_page_1-topic---in-ctw-tree-474-17">&quot;&quot;config_module_self_serve_page_1.topic&quot;&quot; in ctw.tree:474:17</li>
<li id="REF006---config_module_shelly-topic---in-ctw-tree-479-13">&quot;&quot;config_module_shelly.topic&quot;&quot; in ctw.tree:479:13</li>
<li id="REF006---config_module_shelly_page_1-topic---in-ctw-tree-480-17">&quot;&quot;config_module_shelly_page_1.topic&quot;&quot; in ctw.tree:480:17</li>
<li id="REF006---config_module_stripe-topic---in-ctw-tree-485-13">&quot;&quot;config_module_stripe.topic&quot;&quot; in ctw.tree:485:13</li>
<li id="REF006---config_module_stripe_page_1-topic---in-ctw-tree-486-17">&quot;&quot;config_module_stripe_page_1.topic&quot;&quot; in ctw.tree:486:17</li>
<li id="REF006---config_module_virkdata-topic---in-ctw-tree-491-13">&quot;&quot;config_module_virkdata.topic&quot;&quot; in ctw.tree:491:13</li>
<li id="REF006---config_module_virkdata_page_1-topic---in-ctw-tree-492-17">&quot;&quot;config_module_virkdata_page_1.topic&quot;&quot; in ctw.tree:492:17</li>
<li id="REF006---config_module_weatherapi-topic---in-ctw-tree-497-13">&quot;&quot;config_module_weatherapi.topic&quot;&quot; in ctw.tree:497:13</li>
<li id="REF006---config_module_weatherapi_page_1-topic---in-ctw-tree-498-17">&quot;&quot;config_module_weatherapi_page_1.topic&quot;&quot; in ctw.tree:498:17</li>
<li id="REF006---config_module_xlvask-topic---in-ctw-tree-503-13">&quot;&quot;config_module_xlvask.topic&quot;&quot; in ctw.tree:503:13</li>
<li id="REF006---config_module_xlvask_page_1-topic---in-ctw-tree-504-17">&quot;&quot;config_module_xlvask_page_1.topic&quot;&quot; in ctw.tree:504:17</li>
<li id="REF006---modules_module_action_logs-topic---in-ctw-tree-257-13">&quot;&quot;modules_module_action_logs.topic&quot;&quot; in ctw.tree:257:13</li>
<li id="REF006---modules_module_action_logs_page_1-topic---in-ctw-tree-258-17">&quot;&quot;modules_module_action_logs_page_1.topic&quot;&quot; in ctw.tree:258:17</li>
<li id="REF006---modules_module_backup-topic---in-ctw-tree-262-13">&quot;&quot;modules_module_backup.topic&quot;&quot; in ctw.tree:262:13</li>
<li id="REF006---modules_module_backup_page_1-topic---in-ctw-tree-263-17">&quot;&quot;modules_module_backup_page_1.topic&quot;&quot; in ctw.tree:263:17</li>
<li id="REF006---modules_module_cvr-topic---in-ctw-tree-268-13">&quot;&quot;modules_module_cvr.topic&quot;&quot; in ctw.tree:268:13</li>
<li id="REF006---modules_module_cvr_page_1-topic---in-ctw-tree-269-17">&quot;&quot;modules_module_cvr_page_1.topic&quot;&quot; in ctw.tree:269:17</li>
<li id="REF006---modules_module_e_conomic-topic---in-ctw-tree-274-13">&quot;&quot;modules_module_e_conomic.topic&quot;&quot; in ctw.tree:274:13</li>
<li id="REF006---modules_module_e_conomic_page_1-topic---in-ctw-tree-275-17">&quot;&quot;modules_module_e_conomic_page_1.topic&quot;&quot; in ctw.tree:275:17</li>
<li id="REF006---modules_module_entra-topic---in-ctw-tree-288-13">&quot;&quot;modules_module_entra.topic&quot;&quot; in ctw.tree:288:13</li>
<li id="REF006---modules_module_entra_page_1-topic---in-ctw-tree-289-17">&quot;&quot;modules_module_entra_page_1.topic&quot;&quot; in ctw.tree:289:17</li>
<li id="REF006---modules_module_fxratesapi-topic---in-ctw-tree-293-13">&quot;&quot;modules_module_fxratesapi.topic&quot;&quot; in ctw.tree:293:13</li>
<li id="REF006---modules_module_fxratesapi_page_1-topic---in-ctw-tree-294-17">&quot;&quot;modules_module_fxratesapi_page_1.topic&quot;&quot; in ctw.tree:294:17</li>
<li id="REF006---modules_module_motorapi-topic---in-ctw-tree-299-13">&quot;&quot;modules_module_motorapi.topic&quot;&quot; in ctw.tree:299:13</li>
<li id="REF006---modules_module_motorapi_page_1-topic---in-ctw-tree-300-17">&quot;&quot;modules_module_motorapi_page_1.topic&quot;&quot; in ctw.tree:300:17</li>
<li id="REF006---modules_module_self_serve-topic---in-ctw-tree-304-13">&quot;&quot;modules_module_self_serve.topic&quot;&quot; in ctw.tree:304:13</li>
<li id="REF006---modules_module_self_serve_page_1-topic---in-ctw-tree-305-17">&quot;&quot;modules_module_self_serve_page_1.topic&quot;&quot; in ctw.tree:305:17</li>
<li id="REF006---modules_module_stripe-topic---in-ctw-tree-314-13">&quot;&quot;modules_module_stripe.topic&quot;&quot; in ctw.tree:314:13</li>
<li id="REF006---modules_module_stripe_page_1-topic---in-ctw-tree-315-17">&quot;&quot;modules_module_stripe_page_1.topic&quot;&quot; in ctw.tree:315:17</li>
<li id="REF006---modules_module_virkdata-topic---in-ctw-tree-327-13">&quot;&quot;modules_module_virkdata.topic&quot;&quot; in ctw.tree:327:13</li>
<li id="REF006---modules_module_virkdata_page_1-topic---in-ctw-tree-328-17">&quot;&quot;modules_module_virkdata_page_1.topic&quot;&quot; in ctw.tree:328:17</li>
<li id="REF006---modules_module_wash_certificates-topic---in-ctw-tree-332-13">&quot;&quot;modules_module_wash_certificates.topic&quot;&quot; in ctw.tree:332:13</li>
<li id="REF006---modules_module_wash_certificates_page_1-topic---in-ctw-tree-333-17">&quot;&quot;modules_module_wash_certificates_page_1.topic&quot;&quot; in ctw.tree:333:17</li>
<li id="REF006---modules_module_weatherapi-topic---in-ctw-tree-337-13">&quot;&quot;modules_module_weatherapi.topic&quot;&quot; in ctw.tree:337:13</li>
<li id="REF006---modules_module_weatherapi_page_1-topic---in-ctw-tree-338-17">&quot;&quot;modules_module_weatherapi_page_1.topic&quot;&quot; in ctw.tree:338:17</li>
<li id="REF006---modules_module_xlvask-topic---in-ctw-tree-344-13">&quot;&quot;modules_module_xlvask.topic&quot;&quot; in ctw.tree:344:13</li>
<li id="REF006---modules_module_xlvask_page_1-topic---in-ctw-tree-345-17">&quot;&quot;modules_module_xlvask_page_1.topic&quot;&quot; in ctw.tree:345:17</li>
</ul>
<h3 id="TOC001"><code>TOC001</code>&lt;toc-element&gt; points to an article that doesn't exist</h3>
<ul id="TOC001-details">
<li id="TOC001--API_Reference-topic--in-ctw-tree-613-5">&quot;API_Reference.topic&quot; in ctw.tree:613:5</li>
<li id="TOC001--Admin_Bookings_CompleteWithoutWashCertificate_POST-topic--in-ctw-tree-629-5">&quot;Admin_Bookings_CompleteWithoutWashCertificate_POST.topic&quot; in ctw.tree:629:5</li>
<li id="TOC001--Admin_Bookings_Delete_POST-topic--in-ctw-tree-626-5">&quot;Admin_Bookings_Delete_POST.topic&quot; in ctw.tree:626:5</li>
<li id="TOC001--Admin_Bookings_Department_Count_GET-topic--in-ctw-tree-632-5">&quot;Admin_Bookings_Department_Count_GET.topic&quot; in ctw.tree:632:5</li>
<li id="TOC001--Admin_Bookings_Sync_POST-topic--in-ctw-tree-624-5">&quot;Admin_Bookings_Sync_POST.topic&quot; in ctw.tree:624:5</li>
<li id="TOC001--Bookings_Download_PDF_GET-topic--in-ctw-tree-621-5">&quot;Bookings_Download_PDF_GET.topic&quot; in ctw.tree:621:5</li>
<li id="TOC001--Bookings_GET-topic--in-ctw-tree-633-5">&quot;Bookings_GET.topic&quot; in ctw.tree:633:5</li>
<li id="TOC001--Bookings_PUT-topic--in-ctw-tree-628-5">&quot;Bookings_PUT.topic&quot; in ctw.tree:628:5</li>
<li id="TOC001--Customers_GET-topic--in-ctw-tree-618-5">&quot;Customers_GET.topic&quot; in ctw.tree:618:5</li>
<li id="TOC001--Customers_POST-topic--in-ctw-tree-615-5">&quot;Customers_POST.topic&quot; in ctw.tree:615:5</li>
<li id="TOC001--Customers_PUT-topic--in-ctw-tree-608-5">&quot;Customers_PUT.topic&quot; in ctw.tree:608:5</li>
<li id="TOC001--Customers_id_DELETE-topic--in-ctw-tree-610-5">&quot;Customers_id_DELETE.topic&quot; in ctw.tree:610:5</li>
<li id="TOC001--Department_Timebookings_Entries_Public_GET-topic--in-ctw-tree-625-5">&quot;Department_Timebookings_Entries_Public_GET.topic&quot; in ctw.tree:625:5</li>
<li id="TOC001--Department_Timebookings_Entries_Public_POST-topic--in-ctw-tree-620-5">&quot;Department_Timebookings_Entries_Public_POST.topic&quot; in ctw.tree:620:5</li>
<li id="TOC001--Department_Timebookings_OpeningHours_Public_GET-topic--in-ctw-tree-630-5">&quot;Department_Timebookings_OpeningHours_Public_GET.topic&quot; in ctw.tree:630:5</li>
<li id="TOC001--Department_Timebookings_Types_Public_GET-topic--in-ctw-tree-627-5">&quot;Department_Timebookings_Types_Public_GET.topic&quot; in ctw.tree:627:5</li>
<li id="TOC001--Superuser_Bookings_Sync_All_POST-topic--in-ctw-tree-631-5">&quot;Superuser_Bookings_Sync_All_POST.topic&quot; in ctw.tree:631:5</li>
<li id="TOC001--UsageLog_GET-topic--in-ctw-tree-617-5">&quot;UsageLog_GET.topic&quot; in ctw.tree:617:5</li>
<li id="TOC001--User_Bookings_Delete_POST-topic--in-ctw-tree-619-5">&quot;User_Bookings_Delete_POST.topic&quot; in ctw.tree:619:5</li>
<li id="TOC001--User_Bookings_GET-topic--in-ctw-tree-623-5">&quot;User_Bookings_GET.topic&quot; in ctw.tree:623:5</li>
<li id="TOC001--User_Bookings_Washcertificate_Download_POST-topic--in-ctw-tree-622-5">&quot;User_Bookings_Washcertificate_Download_POST.topic&quot; in ctw.tree:622:5</li>
<li id="TOC001--Vehicles_GET-topic--in-ctw-tree-609-5">&quot;Vehicles_GET.topic&quot; in ctw.tree:609:5</li>
<li id="TOC001--Vehicles_POST-topic--in-ctw-tree-616-5">&quot;Vehicles_POST.topic&quot; in ctw.tree:616:5</li>
<li id="TOC001--Vehicles_PUT-topic--in-ctw-tree-607-5">&quot;Vehicles_PUT.topic&quot; in ctw.tree:607:5</li>
<li id="TOC001--Vehicles_id_DELETE-topic--in-ctw-tree-614-5">&quot;Vehicles_id_DELETE.topic&quot; in ctw.tree:614:5</li>
<li id="TOC001--config_module_backups-topic--in-ctw-tree-394-13">&quot;config_module_backups.topic&quot; in ctw.tree:394:13</li>
<li id="TOC001--config_module_backups_page_1-topic--in-ctw-tree-395-17">&quot;config_module_backups_page_1.topic&quot; in ctw.tree:395:17</li>
<li id="TOC001--config_module_bird-topic--in-ctw-tree-400-13">&quot;config_module_bird.topic&quot; in ctw.tree:400:13</li>
<li id="TOC001--config_module_bird_page_1-topic--in-ctw-tree-401-17">&quot;config_module_bird_page_1.topic&quot; in ctw.tree:401:17</li>
<li id="TOC001--config_module_e_conomic-topic--in-ctw-tree-406-13">&quot;config_module_e_conomic.topic&quot; in ctw.tree:406:13</li>
<li id="TOC001--config_module_e_conomic_page_1-topic--in-ctw-tree-407-17">&quot;config_module_e_conomic_page_1.topic&quot; in ctw.tree:407:17</li>
<li id="TOC001--config_module_email-topic--in-ctw-tree-412-13">&quot;config_module_email.topic&quot; in ctw.tree:412:13</li>
<li id="TOC001--config_module_email_page_1-topic--in-ctw-tree-413-17">&quot;config_module_email_page_1.topic&quot; in ctw.tree:413:17</li>
<li id="TOC001--config_module_entra-topic--in-ctw-tree-419-13">&quot;config_module_entra.topic&quot; in ctw.tree:419:13</li>
<li id="TOC001--config_module_entra_page_1-topic--in-ctw-tree-420-17">&quot;config_module_entra_page_1.topic&quot; in ctw.tree:420:17</li>
<li id="TOC001--config_module_fxratesapi-topic--in-ctw-tree-425-13">&quot;config_module_fxratesapi.topic&quot; in ctw.tree:425:13</li>
<li id="TOC001--config_module_fxratesapi_page_1-topic--in-ctw-tree-426-17">&quot;config_module_fxratesapi_page_1.topic&quot; in ctw.tree:426:17</li>
<li id="TOC001--config_module_gatewayapi-topic--in-ctw-tree-431-13">&quot;config_module_gatewayapi.topic&quot; in ctw.tree:431:13</li>
<li id="TOC001--config_module_gatewayapi_page_1-topic--in-ctw-tree-432-17">&quot;config_module_gatewayapi_page_1.topic&quot; in ctw.tree:432:17</li>
<li id="TOC001--config_module_licenseplaterecognizer-topic--in-ctw-tree-437-13">&quot;config_module_licenseplaterecognizer.topic&quot; in ctw.tree:437:13</li>
<li id="TOC001--config_module_licenseplaterecognizer_page_1-topic--in-ctw-tree-438-17">&quot;config_module_licenseplaterecognizer_page_1.topic&quot; in ctw.tree:438:17</li>
<li id="TOC001--config_module_limble-topic--in-ctw-tree-443-13">&quot;config_module_limble.topic&quot; in ctw.tree:443:13</li>
<li id="TOC001--config_module_limble_page_1-topic--in-ctw-tree-444-17">&quot;config_module_limble_page_1.topic&quot; in ctw.tree:444:17</li>
<li id="TOC001--config_module_motorapi-topic--in-ctw-tree-449-13">&quot;config_module_motorapi.topic&quot; in ctw.tree:449:13</li>
<li id="TOC001--config_module_motorapi_page_1-topic--in-ctw-tree-450-17">&quot;config_module_motorapi_page_1.topic&quot; in ctw.tree:450:17</li>
<li id="TOC001--config_module_ocrspace-topic--in-ctw-tree-455-13">&quot;config_module_ocrspace.topic&quot; in ctw.tree:455:13</li>
<li id="TOC001--config_module_ocrspace_page_1-topic--in-ctw-tree-456-17">&quot;config_module_ocrspace_page_1.topic&quot; in ctw.tree:456:17</li>
<li id="TOC001--config_module_openai-topic--in-ctw-tree-461-13">&quot;config_module_openai.topic&quot; in ctw.tree:461:13</li>
<li id="TOC001--config_module_openai_page_1-topic--in-ctw-tree-462-17">&quot;config_module_openai_page_1.topic&quot; in ctw.tree:462:17</li>
<li id="TOC001--config_module_recaptcha-topic--in-ctw-tree-467-13">&quot;config_module_recaptcha.topic&quot; in ctw.tree:467:13</li>
<li id="TOC001--config_module_recaptcha_page_1-topic--in-ctw-tree-468-17">&quot;config_module_recaptcha_page_1.topic&quot; in ctw.tree:468:17</li>
<li id="TOC001--config_module_self_serve-topic--in-ctw-tree-473-13">&quot;config_module_self_serve.topic&quot; in ctw.tree:473:13</li>
<li id="TOC001--config_module_self_serve_page_1-topic--in-ctw-tree-474-17">&quot;config_module_self_serve_page_1.topic&quot; in ctw.tree:474:17</li>
<li id="TOC001--config_module_shelly-topic--in-ctw-tree-479-13">&quot;config_module_shelly.topic&quot; in ctw.tree:479:13</li>
<li id="TOC001--config_module_shelly_page_1-topic--in-ctw-tree-480-17">&quot;config_module_shelly_page_1.topic&quot; in ctw.tree:480:17</li>
<li id="TOC001--config_module_stripe-topic--in-ctw-tree-485-13">&quot;config_module_stripe.topic&quot; in ctw.tree:485:13</li>
<li id="TOC001--config_module_stripe_page_1-topic--in-ctw-tree-486-17">&quot;config_module_stripe_page_1.topic&quot; in ctw.tree:486:17</li>
<li id="TOC001--config_module_virkdata-topic--in-ctw-tree-491-13">&quot;config_module_virkdata.topic&quot; in ctw.tree:491:13</li>
<li id="TOC001--config_module_virkdata_page_1-topic--in-ctw-tree-492-17">&quot;config_module_virkdata_page_1.topic&quot; in ctw.tree:492:17</li>
<li id="TOC001--config_module_weatherapi-topic--in-ctw-tree-497-13">&quot;config_module_weatherapi.topic&quot; in ctw.tree:497:13</li>
<li id="TOC001--config_module_weatherapi_page_1-topic--in-ctw-tree-498-17">&quot;config_module_weatherapi_page_1.topic&quot; in ctw.tree:498:17</li>
<li id="TOC001--config_module_xlvask-topic--in-ctw-tree-503-13">&quot;config_module_xlvask.topic&quot; in ctw.tree:503:13</li>
<li id="TOC001--config_module_xlvask_page_1-topic--in-ctw-tree-504-17">&quot;config_module_xlvask_page_1.topic&quot; in ctw.tree:504:17</li>
<li id="TOC001--modules_module_action_logs-topic--in-ctw-tree-257-13">&quot;modules_module_action_logs.topic&quot; in ctw.tree:257:13</li>
<li id="TOC001--modules_module_action_logs_page_1-topic--in-ctw-tree-258-17">&quot;modules_module_action_logs_page_1.topic&quot; in ctw.tree:258:17</li>
<li id="TOC001--modules_module_backup-topic--in-ctw-tree-262-13">&quot;modules_module_backup.topic&quot; in ctw.tree:262:13</li>
<li id="TOC001--modules_module_backup_page_1-topic--in-ctw-tree-263-17">&quot;modules_module_backup_page_1.topic&quot; in ctw.tree:263:17</li>
<li id="TOC001--modules_module_cvr-topic--in-ctw-tree-268-13">&quot;modules_module_cvr.topic&quot; in ctw.tree:268:13</li>
<li id="TOC001--modules_module_cvr_page_1-topic--in-ctw-tree-269-17">&quot;modules_module_cvr_page_1.topic&quot; in ctw.tree:269:17</li>
<li id="TOC001--modules_module_e_conomic-topic--in-ctw-tree-274-13">&quot;modules_module_e_conomic.topic&quot; in ctw.tree:274:13</li>
<li id="TOC001--modules_module_e_conomic_page_1-topic--in-ctw-tree-275-17">&quot;modules_module_e_conomic_page_1.topic&quot; in ctw.tree:275:17</li>
<li id="TOC001--modules_module_entra-topic--in-ctw-tree-288-13">&quot;modules_module_entra.topic&quot; in ctw.tree:288:13</li>
<li id="TOC001--modules_module_entra_page_1-topic--in-ctw-tree-289-17">&quot;modules_module_entra_page_1.topic&quot; in ctw.tree:289:17</li>
<li id="TOC001--modules_module_fxratesapi-topic--in-ctw-tree-293-13">&quot;modules_module_fxratesapi.topic&quot; in ctw.tree:293:13</li>
<li id="TOC001--modules_module_fxratesapi_page_1-topic--in-ctw-tree-294-17">&quot;modules_module_fxratesapi_page_1.topic&quot; in ctw.tree:294:17</li>
<li id="TOC001--modules_module_motorapi-topic--in-ctw-tree-299-13">&quot;modules_module_motorapi.topic&quot; in ctw.tree:299:13</li>
<li id="TOC001--modules_module_motorapi_page_1-topic--in-ctw-tree-300-17">&quot;modules_module_motorapi_page_1.topic&quot; in ctw.tree:300:17</li>
<li id="TOC001--modules_module_self_serve-topic--in-ctw-tree-304-13">&quot;modules_module_self_serve.topic&quot; in ctw.tree:304:13</li>
<li id="TOC001--modules_module_self_serve_page_1-topic--in-ctw-tree-305-17">&quot;modules_module_self_serve_page_1.topic&quot; in ctw.tree:305:17</li>
<li id="TOC001--modules_module_stripe-topic--in-ctw-tree-314-13">&quot;modules_module_stripe.topic&quot; in ctw.tree:314:13</li>
<li id="TOC001--modules_module_stripe_page_1-topic--in-ctw-tree-315-17">&quot;modules_module_stripe_page_1.topic&quot; in ctw.tree:315:17</li>
<li id="TOC001--modules_module_virkdata-topic--in-ctw-tree-327-13">&quot;modules_module_virkdata.topic&quot; in ctw.tree:327:13</li>
<li id="TOC001--modules_module_virkdata_page_1-topic--in-ctw-tree-328-17">&quot;modules_module_virkdata_page_1.topic&quot; in ctw.tree:328:17</li>
<li id="TOC001--modules_module_wash_certificates-topic--in-ctw-tree-332-13">&quot;modules_module_wash_certificates.topic&quot; in ctw.tree:332:13</li>
<li id="TOC001--modules_module_wash_certificates_page_1-topic--in-ctw-tree-333-17">&quot;modules_module_wash_certificates_page_1.topic&quot; in ctw.tree:333:17</li>
<li id="TOC001--modules_module_weatherapi-topic--in-ctw-tree-337-13">&quot;modules_module_weatherapi.topic&quot; in ctw.tree:337:13</li>
<li id="TOC001--modules_module_weatherapi_page_1-topic--in-ctw-tree-338-17">&quot;modules_module_weatherapi_page_1.topic&quot; in ctw.tree:338:17</li>
<li id="TOC001--modules_module_xlvask-topic--in-ctw-tree-344-13">&quot;modules_module_xlvask.topic&quot; in ctw.tree:344:13</li>
<li id="TOC001--modules_module_xlvask_page_1-topic--in-ctw-tree-345-17">&quot;modules_module_xlvask_page_1.topic&quot; in ctw.tree:345:17</li>
</ul>
<h2 id="warnings">Warnings</h2>
<ul style="list-style: none;">
<li><a href="#INT002"><code>INT002</code> — Map ID is not unique</a></li>
</ul>
<h3 id="INT002"><code>INT002</code> — Map ID is not unique</h3>
<ul id="INT002-details">
<li id="INT002---Bird---Page-1-of-1---in-ctw-tree-589-13--ctw-tree-677-5">&quot;&quot;Bird - Page 1 of 1&quot;&quot; in ctw.tree:589:13; ctw.tree:677:5</li>
<li id="INT002---Bird---in-ctw-tree-588-9--ctw-tree-669-5">&quot;&quot;Bird&quot;&quot; in ctw.tree:588:9; ctw.tree:669:5</li>
<li id="INT002---Bird---Page-1-of-1---in-ctw-tree-589-13--ctw-tree-677-5">&quot;&quot;Bird+-+Page+1+of+1&quot;&quot; in ctw.tree:589:13; ctw.tree:677:5</li>
<li id="INT002---Entra---Page-1-of-1---in-ctw-tree-636-5--ctw-tree-690-5">&quot;&quot;Entra - Page 1 of 1&quot;&quot; in ctw.tree:636:5; ctw.tree:690:5</li>
<li id="INT002---Entra---in-ctw-tree-645-5--ctw-tree-692-5">&quot;&quot;Entra&quot;&quot; in ctw.tree:645:5; ctw.tree:692:5</li>
<li id="INT002---Entra---Page-1-of-1---in-ctw-tree-636-5--ctw-tree-690-5">&quot;&quot;Entra+-+Page+1+of+1&quot;&quot; in ctw.tree:636:5; ctw.tree:690:5</li>
<li id="INT002---FXRatesAPI---Page-1-of-1---in-ctw-tree-663-5--ctw-tree-682-5">&quot;&quot;FXRatesAPI - Page 1 of 1&quot;&quot; in ctw.tree:663:5; ctw.tree:682:5</li>
<li id="INT002---FXRatesAPI---in-ctw-tree-644-5--ctw-tree-651-5">&quot;&quot;FXRatesAPI&quot;&quot; in ctw.tree:644:5; ctw.tree:651:5</li>
<li id="INT002---FXRatesAPI---Page-1-of-1---in-ctw-tree-663-5--ctw-tree-682-5">&quot;&quot;FXRatesAPI+-+Page+1+of+1&quot;&quot; in ctw.tree:663:5; ctw.tree:682:5</li>
<li id="INT002---MotorAPI---Page-1-of-1---in-ctw-tree-659-5--ctw-tree-673-5">&quot;&quot;MotorAPI - Page 1 of 1&quot;&quot; in ctw.tree:659:5; ctw.tree:673:5</li>
<li id="INT002---MotorAPI---in-ctw-tree-648-5--ctw-tree-671-5">&quot;&quot;MotorAPI&quot;&quot; in ctw.tree:648:5; ctw.tree:671:5</li>
<li id="INT002---MotorAPI---Page-1-of-1---in-ctw-tree-659-5--ctw-tree-673-5">&quot;&quot;MotorAPI+-+Page+1+of+1&quot;&quot; in ctw.tree:659:5; ctw.tree:673:5</li>
<li id="INT002---Record-machine-start-button-press-webhook---in-ctw-tree-389-17--ctw-tree-390-17">&quot;&quot;Record machine start button press webhook&quot;&quot; in ctw.tree:389:17; ctw.tree:390:17</li>
<li id="INT002---Record-machine-start-button-press-webhook---in-ctw-tree-389-17--ctw-tree-390-17">&quot;&quot;Record+machine+start+button+press+webhook&quot;&quot; in ctw.tree:389:17; ctw.tree:390:17</li>
<li id="INT002---Self-Serve---Page-1-of-1---in-ctw-tree-649-5--ctw-tree-691-5">&quot;&quot;Self-Serve - Page 1 of 1&quot;&quot; in ctw.tree:649:5; ctw.tree:691:5</li>
<li id="INT002---Self-Serve---in-ctw-tree-527-9--ctw-tree-666-5--ctw-tree-668-5">&quot;&quot;Self-Serve&quot;&quot; in ctw.tree:527:9; ctw.tree:666:5; ctw.tree:668:5</li>
<li id="INT002---Self-Serve---Page-1-of-1---in-ctw-tree-649-5--ctw-tree-691-5">&quot;&quot;Self-Serve+-+Page+1+of+1&quot;&quot; in ctw.tree:649:5; ctw.tree:691:5</li>
<li id="INT002---Stripe---Page-1-of-1---in-ctw-tree-641-5--ctw-tree-683-5">&quot;&quot;Stripe - Page 1 of 1&quot;&quot; in ctw.tree:641:5; ctw.tree:683:5</li>
<li id="INT002---Stripe---in-ctw-tree-650-5--ctw-tree-681-5">&quot;&quot;Stripe&quot;&quot; in ctw.tree:650:5; ctw.tree:681:5</li>
<li id="INT002---Stripe---Page-1-of-1---in-ctw-tree-641-5--ctw-tree-683-5">&quot;&quot;Stripe+-+Page+1+of+1&quot;&quot; in ctw.tree:641:5; ctw.tree:683:5</li>
<li id="INT002---System-wide-search---in-ctw-tree-84-17--ctw-tree-85-17">&quot;&quot;System-wide search&quot;&quot; in ctw.tree:84:17; ctw.tree:85:17</li>
<li id="INT002---System-wide-search---in-ctw-tree-84-17--ctw-tree-85-17">&quot;&quot;System-wide+search&quot;&quot; in ctw.tree:84:17; ctw.tree:85:17</li>
<li id="INT002---VirkData---Page-1-of-1---in-ctw-tree-661-5--ctw-tree-693-5">&quot;&quot;VirkData - Page 1 of 1&quot;&quot; in ctw.tree:661:5; ctw.tree:693:5</li>
<li id="INT002---VirkData---in-ctw-tree-638-5--ctw-tree-695-5">&quot;&quot;VirkData&quot;&quot; in ctw.tree:638:5; ctw.tree:695:5</li>
<li id="INT002---VirkData---Page-1-of-1---in-ctw-tree-661-5--ctw-tree-693-5">&quot;&quot;VirkData+-+Page+1+of+1&quot;&quot; in ctw.tree:661:5; ctw.tree:693:5</li>
<li id="INT002---WeatherAPI---Page-1-of-1---in-ctw-tree-643-5--ctw-tree-646-5">&quot;&quot;WeatherAPI - Page 1 of 1&quot;&quot; in ctw.tree:643:5; ctw.tree:646:5</li>
<li id="INT002---WeatherAPI---in-ctw-tree-658-5--ctw-tree-665-5">&quot;&quot;WeatherAPI&quot;&quot; in ctw.tree:658:5; ctw.tree:665:5</li>
<li id="INT002---WeatherAPI---Page-1-of-1---in-ctw-tree-643-5--ctw-tree-646-5">&quot;&quot;WeatherAPI+-+Page+1+of+1&quot;&quot; in ctw.tree:643:5; ctw.tree:646:5</li>
<li id="INT002---XLVask---Page-1-of-1---in-ctw-tree-639-5--ctw-tree-642-5">&quot;&quot;XLVask - Page 1 of 1&quot;&quot; in ctw.tree:639:5; ctw.tree:642:5</li>
<li id="INT002---XLVask---in-ctw-tree-637-5--ctw-tree-688-5">&quot;&quot;XLVask&quot;&quot; in ctw.tree:637:5; ctw.tree:688:5</li>
<li id="INT002---XLVask---Page-1-of-1---in-ctw-tree-639-5--ctw-tree-642-5">&quot;&quot;XLVask+-+Page+1+of+1&quot;&quot; in ctw.tree:639:5; ctw.tree:642:5</li>
<li id="INT002---e-conomic---Page-1-of-1---in-ctw-tree-672-5--ctw-tree-694-5">&quot;&quot;e-conomic - Page 1 of 1&quot;&quot; in ctw.tree:672:5; ctw.tree:694:5</li>
<li id="INT002---e-conomic---in-ctw-tree-657-5--ctw-tree-685-5">&quot;&quot;e-conomic&quot;&quot; in ctw.tree:657:5; ctw.tree:685:5</li>
<li id="INT002---e-conomic---Page-1-of-1---in-ctw-tree-672-5--ctw-tree-694-5">&quot;&quot;e-conomic+-+Page+1+of+1&quot;&quot; in ctw.tree:672:5; ctw.tree:694:5</li>
</ul>
<h2 id="passed">Passed</h2>
<ul>
<li id="API001"><code>API001</code> — API documentation markup validation issue</li>
<li id="API002"><code>API002</code> — API Reference Generation Error</li>
<li id="API003"><code>API003</code> — Specification file contains features or content not supported by our generator</li>
<li id="API004"><code>API004</code> — API model building problem</li>
<li id="CDE001"><code>CDE001</code> — The &lt;compare&gt; element must contain exactly two code blocks</li>
<li id="CDE002"><code>CDE002</code> — The 'collapsed-title-line-number' attribute value on a 'code-block' element is not a valid number</li>
<li id="CDE003"><code>CDE003</code> — The code snippet doesn't contain the line with the number specified in the 'collapsed-title-line-number' attribute</li>
<li id="CDE004"><code>CDE004</code> — Cannot read the source code snippet from the specified location, falling back to the 'code-block' element content</li>
<li id="CDE005"><code>CDE005</code> — Cannot read the source code snippet from the specified location, and there is no fallback content in the 'code-block' element</li>
<li id="CDE006"><code>CDE006</code> — 'code-block' cannot be empty</li>
<li id="CDE007"><code>CDE007</code> — The 'include-lines' attribute on a 'code-block' element must represent a line number range like 1-3, or multiple comma-separated ranges like 1-3,5-7</li>
<li id="CDE008"><code>CDE008</code> — The code snippet doesn't contain the lines specified in the 'include-lines' attribute of the corresponding 'code-block' element</li>
<li id="CDE009"><code>CDE009</code> — Cannot use both the 'include-symbol' and the 'include-lines' attributes to reference a code snippet</li>
<li id="CDE010"><code>CDE010</code> — The specified code construct is not found in the source file or the file language is not recognized</li>
<li id="CDE011"><code>CDE011</code> — Code sample contains errors</li>
<li id="CDE012"><code>CDE012</code> — Cannot validate code sample because its language is not supported</li>
<li id="CDE013"><code>CDE013</code> — Cannot validate code sample because its language is not specified</li>
<li id="CDE014"><code>CDE014</code> — Content other than text or CDATA in a code block, for example, HTML/XML-like tags</li>
<li id="CDE015"><code>CDE015</code> — Unsupported 'style' value on the &lt;compare&gt; element</li>
<li id="CDE016"><code>CDE016</code> — Unknown language is specified for a code block</li>
<li id="CDE017"><code>CDE017</code> — Syntax error in a code block</li>
<li id="CDE018"><code>CDE018</code> — Expected at least one closed @start...@end block, but found none.</li>
<li id="CNF001"><code>CNF001</code> — Category specified in &lt;seealso&gt; is not declared in c.list</li>
<li id="CNF002"><code>CNF002</code> — Topic title cannot be empty</li>
<li id="CNF003"><code>CNF003</code> — Invalid buildprofiles.xml property value</li>
<li id="CNF005"><code>CNF005</code> — The 'accepts-web-file-names' attribute contains symbols that cannot appear in a file name</li>
<li id="CNF006"><code>CNF006</code> — The 'accepts-web-file-names' attribute points to a non-existing redirection rule</li>
<li id="CNF008"><code>CNF008</code> — The 'start-page' attribute on 'instance-profile' cannot be empty</li>
<li id="CNF009"><code>CNF009</code> — Global variable name in v.list is duplicated</li>
<li id="CNF010"><code>CNF010</code> — Cannot find specified resource in the resources folder</li>
<li id="CNF011"><code>CNF011</code> — Category sort order is not a valid number</li>
<li id="CNF012"><code>CNF012</code> — Tooltip not found</li>
<li id="CNF013"><code>CNF013</code> — Cannot reference a resource as the r.list file is missing</li>
<li id="CNF014"><code>CNF014</code> — The instance version links require 'web-path' in instance declaration</li>
<li id="CNF015"><code>CNF015</code> — Cannot write the result to the specified output directory</li>
<li id="CNF016"><code>CNF016</code> — Redirect map ID is not unique</li>
<li id="CNF017"><code>CNF017</code> — Cannot read analytics script or html snippet</li>
<li id="CNF018"><code>CNF018</code> — Footer link without 'href' attribute</li>
<li id="CNF019"><code>CNF019</code> — Unknown social link type {0}</li>
<li id="CNF020"><code>CNF020</code> — Absolute images web-path was given, but images are bundled into single artifact</li>
<li id="CNF021"><code>CNF021</code> — Web filename is reserved, use a different value for the &lt;web-file-name&gt; tag</li>
<li id="CNF022"><code>CNF022</code> — Web filename is duplicated, ensure that web filename is unique</li>
<li id="CNF023"><code>CNF023</code> — Topic filenames must be unique, regardless of letter case</li>
<li id="CNF024"><code>CNF024</code> — The 'start-page' attribute on 'instance-profile' is empty</li>
<li id="CNF025"><code>CNF025</code> — Web filename is empty. Add letters or digits to the topic filename or specify a custom web filename</li>
<li id="CNF026"><code>CNF026</code> — Referenced file {0} does not exist or is not possible to read.</li>
<li id="CNF027"><code>CNF027</code> — Multiple values for {0} are not allowed here.</li>
<li id="CNF028"><code>CNF028</code> — Expected true or false.</li>
<li id="CTT001"><code>CTT001</code> — Inappropriate language detected</li>
<li id="CTT002"><code>CTT002</code> — The generated HTML file size exceeds 1 MB, which may lead to poor performance when rendered in the browser. Consider splitting large topics into multiple smaller ones.</li>
<li id="CTT004"><code>CTT004</code> — Undefined variable</li>
<li id="CTT005"><code>CTT005</code> — Variable depends on itself</li>
<li id="INT001"><code>INT001</code> — Error description template is not found</li>
<li id="INT004"><code>INT004</code> — Unexpected article rendering error:</li>
<li id="INT007"><code>INT007</code> — XML serialization error</li>
<li id="INT008"><code>INT008</code> — Unexpected HTML rendering error: cannot produce a PDF</li>
<li id="INT009"><code>INT009</code> — Unexpected diagram rendering error</li>
<li id="INT010"><code>INT010</code> — Unexpected PlantUML error:</li>
<li id="MRK001"><code>MRK001</code> — The element doesn't comply with validation rules</li>
<li id="MRK002"><code>MRK002</code> — Source file syntax is corrupted</li>
<li id="MRK003"><code>MRK003</code> — Element ID is not unique</li>
<li id="MRK006"><code>MRK006</code> — The 'term' attribute is missing for the &lt;tooltip&gt; element</li>
<li id="MRK007"><code>MRK007</code> — The 'resource-id' attribute is missing for the &lt;res&gt; element</li>
<li id="MRK008"><code>MRK008</code> — The 'ref' attribute is missing for the &lt;category&gt; element</li>
<li id="MRK009"><code>MRK009</code> — Element is not allowed in the current context</li>
<li id="MRK011"><code>MRK011</code> — Only &lt;code&gt;, &lt;property&gt;, &lt;shortcut&gt;, image or text with inline formatting is allowed inside an &lt;a&gt; element to define link text</li>
<li id="MRK012"><code>MRK012</code> — Element is unknown</li>
<li id="MRK013"><code>MRK013</code> — Starting page title is empty</li>
<li id="MRK014"><code>MRK014</code> — Section starting page description is empty</li>
<li id="MRK015"><code>MRK015</code> — Section starting page must contain exactly two links under the 'spotlight' element</li>
<li id="MRK016"><code>MRK016</code> — Section starting page link summary is empty</li>
<li id="MRK017"><code>MRK017</code> — Section starting page group title is empty</li>
<li id="MRK018"><code>MRK018</code> — Section starting page group is empty</li>
<li id="MRK019"><code>MRK019</code> — Element is not allowed on a section starting page</li>
<li id="MRK020"><code>MRK020</code> — Javascript expression specified in the 'use-when' attribute failed to execute</li>
<li id="MRK026"><code>MRK026</code> — Unknown 'type' attribute value</li>
<li id="MRK027"><code>MRK027</code> — The source file for the 'include' is corrupted</li>
<li id="MRK028"><code>MRK028</code> — Invalid 'style' attribute value on a deflist</li>
<li id="MRK032"><code>MRK032</code> — Element ID contains whitespace characters</li>
<li id="MRK033"><code>MRK033</code> — Unknown 'style' attribute value on a list</li>
<li id="MRK034"><code>MRK034</code> — The value of the 'columns' attribute on a list must be a number between 1 and 5</li>
<li id="MRK035"><code>MRK035</code> — The value of the 'start' attribute on a list must be a valid number</li>
<li id="MRK036"><code>MRK036</code> — The 'start' attribute on a list is only valid for 'alpha-lower' and 'decimal' list types</li>
<li id="MRK037"><code>MRK037</code> — Unknown 'sorted' attribute value</li>
<li id="MRK038"><code>MRK038</code> — The value of the 'level' attribute on a chapter must be a valid number between 2 and 6</li>
<li id="MRK039"><code>MRK039</code> — Unknown 'style' attribute value on a procedure</li>
<li id="MRK040"><code>MRK040</code> — Tab with an empty title</li>
<li id="MRK041"><code>MRK041</code> — Unknown 'style' attribute value on a table</li>
<li id="MRK042"><code>MRK042</code> — 'colspan' and 'rowspan' attributes of a table cell must be valid numbers</li>
<li id="MRK043"><code>MRK043</code> — Cannot sort table with 'colspan' or 'rowspan'</li>
<li id="MRK044"><code>MRK044</code> — Cannot sort table with rows of different length</li>
<li id="MRK045"><code>MRK045</code> — 'sorted' attribute must appear on the first row cells</li>
<li id="MRK046"><code>MRK046</code> — Element referred from 'rel' attribute does not exist or is inaccessible in the current context</li>
<li id="MRK047"><code>MRK047</code> — Summary element has both 'rel' attribute and text specified</li>
<li id="MRK048"><code>MRK048</code> — Summary element does not provide any text</li>
<li id="MRK049"><code>MRK049</code> — Unknown 'style' attribute value on a format element</li>
<li id="MRK050"><code>MRK050</code> — Unknown 'color' attribute value on a format element</li>
<li id="MRK051"><code>MRK051</code> — The topic must contain exactly one &lt;tldr&gt; element. All except the first one were ignored</li>
<li id="MRK052"><code>MRK052</code> — Web name is empty</li>
<li id="MRK053"><code>MRK053</code> — Element has no title</li>
<li id="MRK054"><code>MRK054</code> — Collapsible procedure title is empty</li>
<li id="MRK055"><code>MRK055</code> — Unknown 'caps' attribute value</li>
<li id="MRK056"><code>MRK056</code> — Title can not contain inline formatting</li>
<li id="MRK057"><code>MRK057</code> — Paragraph can only contain inline elements</li>
<li id="MRK058"><code>MRK058</code> — Large image in paragraph rendered as a block element by default. Put it outside the paragraph or set the 'style' attribute to indicate your intent.</li>
<li id="MRK059"><code>MRK059</code> — Nested paragraphs are not allowed. Consider unwrapping or properly breaking the paragraphs.</li>
<li id="MRK060"><code>MRK060</code> — The chapter must contain exactly one &lt;tldr&gt; element. All except the first one were ignored</li>
<li id="MRK061"><code>MRK061</code> — Chapter titles with level more than 6 are rendered as level 6</li>
<li id="MRK062"><code>MRK062</code> — The value of the 'depth' attribute on a &lt;toc&gt; must be a valid number</li>
<li id="MRK063"><code>MRK063</code> — The 'src' attribute is missing for the &lt;resource&gt; element</li>
<li id="MRK064"><code>MRK064</code> — The topic must contain exactly one &lt;primary-label&gt; element. All except the first one were ignored</li>
<li id="MRK065"><code>MRK065</code> — The chapter must contain exactly one &lt;primary-label&gt; element. All except the first one were ignored</li>
<li id="MRK066"><code>MRK066</code> — Unknown 'border' attribute value on a table. It should be 'false' for no border or 'true' for a border</li>
<li id="MRK067"><code>MRK067</code> — The 'height' attribute of an iframe cannot be set to 100%. Height will be set to 250px</li>
<li id="MRK068"><code>MRK068</code> — A 'card' can have only one of the following attributes: 'image', 'icon', or 'badge'</li>
<li id="MRK069"><code>MRK069</code> — Last modified date must be either a specific date in the format YYYY-MM-DD, 'git' (to get the date from Git history) or 'file' (to get the date from the file system)</li>
<li id="PTY001"><code>PTY001</code> — The &lt;property&gt; element must contain the 'bundle' and 'key' attributes</li>
<li id="PTY002"><code>PTY002</code> — The property bundle referenced from a &lt;property&gt; element cannot be found</li>
<li id="PTY003"><code>PTY003</code> — The specified property key cannot be found in the bundle specified by &lt;property&gt; element</li>
<li id="PTY004"><code>PTY004</code> — Product specified in the 'from-product' attribute on the &lt;property&gt; element is not found</li>
<li id="PTY005"><code>PTY005</code> — The specified property bundle is not available for the current product</li>
<li id="PTY006"><code>PTY006</code> — Cannot process regular expression that cleans up a property value</li>
<li id="PTY007"><code>PTY007</code> — Property content is corrupted</li>
<li id="REF001"><code>REF001</code> — Cannot link to a topic that is not included in the current instance</li>
<li id="REF002"><code>REF002</code> — Referenced topic doesn't exist</li>
<li id="REF003"><code>REF003</code> — Cannot include element with the specified ID because it does not exist</li>
<li id="REF004"><code>REF004</code> — Link uses anchor that does not exist</li>
<li id="REF005"><code>REF005</code> — Link inside topic points to the same topic without an anchor</li>
<li id="REF007"><code>REF007</code> — Cannot redirect from file name that is associated with an existing article</li>
<li id="REF008"><code>REF008</code> — Link points to a topic in a 'draft' state that is not included in the current build</li>
<li id="REF009"><code>REF009</code> — Cannot include from topic that does not exist</li>
<li id="REF010"><code>REF010</code> — Link in the 'seealso' section points to itself</li>
<li id="REF012"><code>REF012</code> — Unknown 'style' attribute value on a 'seealso' section</li>
<li id="REF013"><code>REF013</code> — Cannot include a parent element in its child element</li>
<li id="REF014"><code>REF014</code> — Cannot include an include directly</li>
<li id="REF015"><code>REF015</code> — Include points to multiple IDs</li>
<li id="SCT001"><code>SCT001</code> — Hard-coded shortcut: consider referencing the corresponding action in the 'key' attribute of the 'shortcut' element</li>
<li id="SCT002"><code>SCT002</code> — Shortcut is not defined for the requested keymap</li>
<li id="SCT003"><code>SCT003</code> — Shortcut is not defined for the requested platform</li>
<li id="SCT004"><code>SCT004</code> — The file containing the keymap referenced from platforms.xml cannot be found or is empty</li>
<li id="SCT005"><code>SCT005</code> — Action ID is not found in the product keymap.</li>
<li id="SCT006"><code>SCT006</code> — Shortcut is not defined for layout</li>
<li id="SCT007"><code>SCT007</code> — Shortcut is not defined for the default keymap</li>
<li id="SCT008"><code>SCT008</code> — Shortcut is not defined in the current product's keymap</li>
<li id="SCT009"><code>SCT009</code> — The requested keymap cannot be found</li>
<li id="SCT011"><code>SCT011</code> — platforms.xml file is corrupted</li>
<li id="TOC002"><code>TOC002</code>&lt;toc-element&gt; points to a library topic</li>
<li id="TOC003"><code>TOC003</code> — The nesting level for a &lt;toc-element&gt; is more than 3. Consider restructuring content for easier navigation.</li>
<li id="TOC004"><code>TOC004</code> — Wrapper &lt;toc-element&gt; cannot have the 'accepts-web-file-names' attribute as it does not produce any content, so it cannot take redirects</li>
<li id="TOC005"><code>TOC005</code> — Wrapper &lt;toc-element&gt; cannot have the 'help-id' attribute as it does not produce any content, so there is nothing to link from the UI</li>
<li id="TOC006"><code>TOC006</code>&lt;toc-element&gt; ID is duplicated</li>
<li id="TOC007"><code>TOC007</code> — The 'toc-title' attribute is redundant as it matches the topic title</li>
<li id="TOC010"><code>TOC010</code>&lt;toc-element&gt; cannot have both the 'accepts-web-file-names' and the 'accepts-web-file-names-ref' attribute. Consider moving all references to redirection-rules.xml</li>
<li id="TOC011"><code>TOC011</code> — The 'accepts-web-file-names' attribute references the same topic in several TOC elements</li>
<li id="TOC012"><code>TOC012</code> — The 'custom-title' attribute on a &lt;toc-element&gt; is deprecated. Move it to the topic itself.</li>
<li id="TOC013"><code>TOC013</code> — The value of the 'show-structure-depth' attribute on a &lt;toc-element&gt; must be a valid number</li>
<li id="TOC016"><code>TOC016</code>&lt;toc-element&gt; should have one of following attributes: 'topic', 'toc-title', or 'ref'</li>
<li id="TOC017"><code>TOC017</code> — Invalid value of attribute &quot;for&quot;</li>
<li id="TOC018"><code>TOC018</code> — Target for external redirect conflicts with topic reference</li>
<li id="TOC019"><code>TOC019</code> — Target for external redirect requires web file names</li>
<li id="TOC020"><code>TOC020</code> — Target for external redirect must be a hidden TOC element</li>
<li id="VIS001"><code>VIS001</code> — Image or video file cannot be found</li>
<li id="VIS002"><code>VIS002</code> — GIF animations and image thumbnails can only be block elements, do not place them inside a paragraph</li>
<li id="VIS003"><code>VIS003</code> — Logo aspect ratio H:W must be between 0.24 and 1.20</li>
<li id="VIS004"><code>VIS004</code> — Image or video must have the 'src' attribute</li>
<li id="VIS005"><code>VIS005</code> — Origin module for image cannot be found in the project</li>
<li id="VIS006"><code>VIS006</code> — Image or video 'width' and 'height' attributes must be positive integers</li>
<li id="VIS007"><code>VIS007</code> — Unable to count frames in a GIF image</li>
<li id="VIS008"><code>VIS008</code> — GIF animations cannot be rendered without a border, do not use 'border-effect=&quot;none&quot;'</li>
<li id="VIS009"><code>VIS009</code> — Unknown image 'border-effect' value</li>
<li id="VIS010"><code>VIS010</code> — Unknown image style</li>
<li id="VIS011"><code>VIS011</code> — Dark version of image or video file cannot be found</li>
<li id="VIS012"><code>VIS012</code> — Image file type is not supported, must be PNG or SVG</li>
<li id="VIS013"><code>VIS013</code> — Video file type is not supported, must be MP4</li>
<li id="VIS014"><code>VIS014</code> — Referenced file is outside the documentation project</li>
<li id="VIS015"><code>VIS015</code> — Image file type is not supported</li>
</ul>
</body>
</html>
File diff suppressed because it is too large Load Diff
Binary file not shown.
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -0,0 +1,38 @@
<!DOCTYPE html SYSTEM "about:legacy-compat">
<html lang="en-US" data-preset="contrast" data-primary-color="#307FFF"><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8"><meta charset="UTF-8"><meta name="robots" content="noindex"><meta name="built-on" content="2026-03-17T15:38:46.389471778"><title>Add branding option | Copenhagen Truck Wash API</title><script type="application/json" id="virtual-toc-data">[{"id":"endpoint","level":0,"title":"Endpoint","anchor":"#endpoint"},{"id":"operation","level":0,"title":"Operation","anchor":"#operation"},{"id":"authentication","level":0,"title":"Authentication","anchor":"#authentication"},{"id":"request-body","level":0,"title":"Request Body","anchor":"#request-body"},{"id":"responses","level":0,"title":"Responses","anchor":"#responses"}]</script><script type="application/json" id="topic-shortcuts"></script><link href="https://resources.jetbrains.com/writerside/apidoc/6.26.0-b913/app.css" rel="stylesheet"><meta name="msapplication-TileColor" content="#000000"><link rel="apple-touch-icon" sizes="180x180" href="https://jetbrains.com/apple-touch-icon.png"><link rel="icon" type="image/png" sizes="32x32" href="https://jetbrains.com/favicon-32x32.png"><link rel="icon" type="image/png" sizes="16x16" href="https://jetbrains.com/favicon-16x16.png"><meta name="msapplication-TileImage" content="https://resources.jetbrains.com/storage/ui/favicons/mstile-144x144.png"><meta name="msapplication-square70x70logo" content="https://resources.jetbrains.com/storage/ui/favicons/mstile-70x70.png"><meta name="msapplication-square150x150logo" content="https://resources.jetbrains.com/storage/ui/favicons/mstile-150x150.png"><meta name="msapplication-wide310x150logo" content="https://resources.jetbrains.com/storage/ui/favicons/mstile-310x150.png"><meta name="msapplication-square310x310logo" content="https://resources.jetbrains.com/storage/ui/favicons/mstile-310x310.png"><meta name="image" content=""><!-- Open Graph --><meta property="og:title" content="Add branding option | Copenhagen Truck Wash API"><meta property="og:description" content=""><meta property="og:image" content=""><meta property="og:site_name" content="Copenhagen Truck Wash API Help"><meta property="og:type" content="website"><meta property="og:locale" content="en_US"><meta property="og:url" content="writerside-documentation/addbrandingoption.html"><!-- End Open Graph --><!-- Twitter Card --><meta name="twitter:card" content="summary_large_image"><meta name="twitter:site" content=""><meta name="twitter:title" content="Add branding option | Copenhagen Truck Wash API"><meta name="twitter:description" content=""><meta name="twitter:creator" content=""><meta name="twitter:image:src" content=""><!-- End Twitter Card --><!-- Schema.org WebPage --><script type="application/ld+json">{
"@context": "http://schema.org",
"@type": "WebPage",
"@id": "writerside-documentation/addbrandingoption.html#webpage",
"url": "writerside-documentation/addbrandingoption.html",
"name": "Add branding option | Copenhagen Truck Wash API",
"description": "",
"image": "",
"inLanguage":"en-US"
}</script><!-- End Schema.org --><!-- Schema.org WebSite --><script type="application/ld+json">{
"@type": "WebSite",
"@id": "writerside-documentation/#website",
"url": "writerside-documentation/",
"name": "Copenhagen Truck Wash API Help"
}</script><!-- End Schema.org --></head><body data-id="addBrandingOption" data-main-title="Add branding option" data-article-props="{&quot;seeAlsoStyle&quot;:&quot;links&quot;}" data-template="article" data-breadcrumbs="API-Reference.topic|API Reference///Tag_Branding.topic|Branding///Tag_Branding_Page_1.topic|Branding - Page 1 of 1"><div class="wrapper"><main class="panel _main"><header class="panel__header"><div class="container"><h3>Copenhagen Truck Wash API Help</h3><div class="panel-trigger"></div></div></header><section class="panel__content"><div class="container"><article class="article" data-shortcut-switcher="inactive"><h1 data-toc="addBrandingOption" id="addBrandingOption.topic">Add branding option</h1><p id="-8moxql_2">This endpoint documentation is generated directly from <code class="code" id="-8moxql_8">openapi.yaml</code>.</p><section class="chapter"><h2 id="endpoint" data-toc="endpoint">Endpoint</h2><div class="code-block" data-lang="http">POST /branding</div></section><section class="chapter"><h2 id="operation" data-toc="operation">Operation</h2><p id="-8moxql_10">Operation ID: <code class="code" id="-8moxql_12">addBrandingOption</code></p><p id="-8moxql_11">Create a new branding option</p></section><section class="chapter"><h2 id="authentication" data-toc="authentication">Authentication</h2><p id="-8moxql_13">Security requirements:</p><div class="table-wrapper"><table class="wide" id="-8moxql_14"><thead><tr class="ijRowHead" id="-8moxql_15"><th id="-8moxql_17"><p>Scheme</p></th><th id="-8moxql_18"><p>Scopes</p></th></tr></thead><tbody><tr id="-8moxql_16"><td id="-8moxql_19"><p>BearerAuth</p></td><td id="-8moxql_20"><p>-</p></td></tr></tbody></table></div></section><section class="chapter"><h2 id="request-body" data-toc="request-body">Request Body</h2><p id="-8moxql_21">Required: yes.</p><p id="-8moxql_22">Content type: <code class="code" id="-8moxql_24">application/json</code></p><div class="code-block" data-lang="json">
{
&quot;properties&quot;: {
&quot;cvr&quot;: {
&quot;type&quot;: &quot;integer&quot;
},
&quot;description&quot;: {
&quot;type&quot;: &quot;string&quot;
},
&quot;name&quot;: {
&quot;type&quot;: &quot;string&quot;
}
},
&quot;required&quot;: [
&quot;name&quot;,
&quot;description&quot;,
&quot;cvr&quot;
],
&quot;type&quot;: &quot;object&quot;
}
</div></section><section class="chapter"><h2 id="responses" data-toc="responses">Responses</h2><div class="table-wrapper"><table class="wide" id="-8moxql_25"><thead><tr class="ijRowHead" id="-8moxql_28"><th id="-8moxql_31"><p>Status</p></th><th id="-8moxql_32"><p>Description</p></th><th id="-8moxql_33"><p>Content Types</p></th></tr></thead><tbody><tr id="-8moxql_29"><td id="-8moxql_34"><p>200</p></td><td id="-8moxql_35"><p>Branding option added successfully</p></td><td id="-8moxql_36"><p>application/json</p></td></tr><tr id="-8moxql_30"><td id="-8moxql_37"><p>403</p></td><td id="-8moxql_38"></td><td id="-8moxql_39"></td></tr></tbody></table></div><p id="-8moxql_26">Schema for response <code class="code" id="-8moxql_40">200</code> (<code class="code" id="-8moxql_41">application/json</code>):</p><div class="code-block" data-lang="json">
{}
</div></section><div class="last-modified">17 March 2026</div><div data-feedback-placeholder="true"></div><div class="navigation-links _bottom"><a href="listbrandingoptions.html" class="navigation-links__prev">List branding options</a><a href="editbrandingoption.html" class="navigation-links__next">Edit branding option</a></div></article><div id="disqus_thread"></div></div></section></main></div><script src="https://resources.jetbrains.com/writerside/apidoc/6.26.0-b913/app.js"></script></body></html>
@@ -0,0 +1,20 @@
<!DOCTYPE html SYSTEM "about:legacy-compat">
<html lang="en-US" data-preset="contrast" data-primary-color="#307FFF"><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8"><meta charset="UTF-8"><meta name="robots" content="noindex"><meta name="built-on" content="2026-03-17T15:43:46.652552977"><title>Record machine start button press webhook | Copenhagen Truck Wash API</title><script type="application/json" id="virtual-toc-data">[{"id":"endpoint","level":0,"title":"Endpoint","anchor":"#endpoint"},{"id":"operation","level":0,"title":"Operation","anchor":"#operation"},{"id":"authentication","level":0,"title":"Authentication","anchor":"#authentication"},{"id":"parameters","level":0,"title":"Parameters","anchor":"#parameters"},{"id":"responses","level":0,"title":"Responses","anchor":"#responses"}]</script><script type="application/json" id="topic-shortcuts"></script><link href="https://resources.jetbrains.com/writerside/apidoc/6.26.0-b913/app.css" rel="stylesheet"><meta name="msapplication-TileColor" content="#000000"><link rel="apple-touch-icon" sizes="180x180" href="https://jetbrains.com/apple-touch-icon.png"><link rel="icon" type="image/png" sizes="32x32" href="https://jetbrains.com/favicon-32x32.png"><link rel="icon" type="image/png" sizes="16x16" href="https://jetbrains.com/favicon-16x16.png"><meta name="msapplication-TileImage" content="https://resources.jetbrains.com/storage/ui/favicons/mstile-144x144.png"><meta name="msapplication-square70x70logo" content="https://resources.jetbrains.com/storage/ui/favicons/mstile-70x70.png"><meta name="msapplication-square150x150logo" content="https://resources.jetbrains.com/storage/ui/favicons/mstile-150x150.png"><meta name="msapplication-wide310x150logo" content="https://resources.jetbrains.com/storage/ui/favicons/mstile-310x150.png"><meta name="msapplication-square310x310logo" content="https://resources.jetbrains.com/storage/ui/favicons/mstile-310x310.png"><meta name="image" content=""><!-- Open Graph --><meta property="og:title" content="Record machine start button press webhook | Copenhagen Truck Wash API"><meta property="og:description" content=""><meta property="og:image" content=""><meta property="og:site_name" content="Copenhagen Truck Wash API Help"><meta property="og:type" content="website"><meta property="og:locale" content="en_US"><meta property="og:url" content="writerside-documentation/addbuttonpress.html"><!-- End Open Graph --><!-- Twitter Card --><meta name="twitter:card" content="summary_large_image"><meta name="twitter:site" content=""><meta name="twitter:title" content="Record machine start button press webhook | Copenhagen Truck Wash API"><meta name="twitter:description" content=""><meta name="twitter:creator" content=""><meta name="twitter:image:src" content=""><!-- End Twitter Card --><!-- Schema.org WebPage --><script type="application/ld+json">{
"@context": "http://schema.org",
"@type": "WebPage",
"@id": "writerside-documentation/addbuttonpress.html#webpage",
"url": "writerside-documentation/addbuttonpress.html",
"name": "Record machine start button press webhook | Copenhagen Truck Wash API",
"description": "",
"image": "",
"inLanguage":"en-US"
}</script><!-- End Schema.org --><!-- Schema.org WebSite --><script type="application/ld+json">{
"@type": "WebSite",
"@id": "writerside-documentation/#website",
"url": "writerside-documentation/",
"name": "Copenhagen Truck Wash API Help"
}</script><!-- End Schema.org --></head><body data-id="addButtonPress" data-main-title="Record machine start button press webhook" data-article-props="{&quot;seeAlsoStyle&quot;:&quot;links&quot;}" data-template="article" data-breadcrumbs="API-Reference.topic|API Reference///Tag_Plate_Scans.topic|Plate Scans///Tag_Plate_Scans_Page_1.topic|Plate Scans - Page 1 of 1"><div class="wrapper"><main class="panel _main"><header class="panel__header"><div class="container"><h3>Copenhagen Truck Wash API Help</h3><div class="panel-trigger"></div></div></header><section class="panel__content"><div class="container"><article class="article" data-shortcut-switcher="inactive"><h1 data-toc="addButtonPress" id="addButtonPress.topic">Record machine start button press webhook</h1><p id="-d80xc_2">This endpoint documentation is generated directly from <code class="code" id="-d80xc_8">openapi.yaml</code>.</p><section class="chapter"><h2 id="endpoint" data-toc="endpoint">Endpoint</h2><div class="code-block" data-lang="http">GET /relay/button/press/post</div></section><section class="chapter"><h2 id="operation" data-toc="operation">Operation</h2><p id="-d80xc_10">Operation ID: <code class="code" id="-d80xc_12">addButtonPress</code></p><p id="-d80xc_11">Record machine start button press webhook</p></section><section class="chapter"><h2 id="authentication" data-toc="authentication">Authentication</h2><p id="-d80xc_13">Security requirements:</p><div class="table-wrapper"><table class="wide" id="-d80xc_14"><thead><tr class="ijRowHead" id="-d80xc_15"><th id="-d80xc_17"><p>Scheme</p></th><th id="-d80xc_18"><p>Scopes</p></th></tr></thead><tbody><tr id="-d80xc_16"><td id="-d80xc_19"><p>BearerAuth</p></td><td id="-d80xc_20"><p>-</p></td></tr></tbody></table></div></section><section class="chapter"><h2 id="parameters" data-toc="parameters">Parameters</h2><div class="table-wrapper"><table class="wide" id="-d80xc_21"><thead><tr class="ijRowHead" id="-d80xc_22"><th id="-d80xc_26"><p>Name</p></th><th id="-d80xc_27"><p>In</p></th><th id="-d80xc_28"><p>Required</p></th><th id="-d80xc_29"><p>Type</p></th><th id="-d80xc_30"><p>Description</p></th></tr></thead><tbody><tr id="-d80xc_23"><td id="-d80xc_31"><p>token</p></td><td id="-d80xc_32"><p>query</p></td><td id="-d80xc_33"><p>no</p></td><td id="-d80xc_34"><p>string</p></td><td id="-d80xc_35"></td></tr><tr id="-d80xc_24"><td id="-d80xc_36"><p>lane_id</p></td><td id="-d80xc_37"><p>query</p></td><td id="-d80xc_38"><p>no</p></td><td id="-d80xc_39"><p>integer</p></td><td id="-d80xc_40"></td></tr><tr id="-d80xc_25"><td id="-d80xc_41"><p>reg</p></td><td id="-d80xc_42"><p>query</p></td><td id="-d80xc_43"><p>no</p></td><td id="-d80xc_44"><p>string</p></td><td id="-d80xc_45"></td></tr></tbody></table></div></section><section class="chapter"><h2 id="responses" data-toc="responses">Responses</h2><div class="table-wrapper"><table class="wide" id="-d80xc_46"><thead><tr class="ijRowHead" id="-d80xc_49"><th id="-d80xc_52"><p>Status</p></th><th id="-d80xc_53"><p>Description</p></th><th id="-d80xc_54"><p>Content Types</p></th></tr></thead><tbody><tr id="-d80xc_50"><td id="-d80xc_55"><p>201</p></td><td id="-d80xc_56"><p>Button press recorded and linked to a self-serve wash session</p></td><td id="-d80xc_57"><p>application/json</p></td></tr><tr id="-d80xc_51"><td id="-d80xc_58"><p>404</p></td><td id="-d80xc_59"></td><td id="-d80xc_60"></td></tr></tbody></table></div><p id="-d80xc_47">Schema for response <code class="code" id="-d80xc_61">201</code> (<code class="code" id="-d80xc_62">application/json</code>):</p><div class="code-block" data-lang="json">
{
&quot;$ref&quot;: &quot;#/components/schemas/MachineButtonPressWebhookResponse&quot;
}
</div></section><div class="last-modified">17 March 2026</div><div data-feedback-placeholder="true"></div><div class="navigation-links _bottom"><a href="getplatescanpostresults.html" class="navigation-links__prev">Get post-scan results</a><a href="addbuttonpresspost.html" class="navigation-links__next">Record machine start button press webhook</a></div></article><div id="disqus_thread"></div></div></section></main></div><script src="https://resources.jetbrains.com/writerside/apidoc/6.26.0-b913/app.js"></script></body></html>
@@ -0,0 +1,35 @@
<!DOCTYPE html SYSTEM "about:legacy-compat">
<html lang="en-US" data-preset="contrast" data-primary-color="#307FFF"><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8"><meta charset="UTF-8"><meta name="robots" content="noindex"><meta name="built-on" content="2026-03-17T15:43:55.703970971"><title>Record machine start button press webhook | Copenhagen Truck Wash API</title><script type="application/json" id="virtual-toc-data">[{"id":"endpoint","level":0,"title":"Endpoint","anchor":"#endpoint"},{"id":"operation","level":0,"title":"Operation","anchor":"#operation"},{"id":"authentication","level":0,"title":"Authentication","anchor":"#authentication"},{"id":"request-body","level":0,"title":"Request Body","anchor":"#request-body"},{"id":"responses","level":0,"title":"Responses","anchor":"#responses"}]</script><script type="application/json" id="topic-shortcuts"></script><link href="https://resources.jetbrains.com/writerside/apidoc/6.26.0-b913/app.css" rel="stylesheet"><meta name="msapplication-TileColor" content="#000000"><link rel="apple-touch-icon" sizes="180x180" href="https://jetbrains.com/apple-touch-icon.png"><link rel="icon" type="image/png" sizes="32x32" href="https://jetbrains.com/favicon-32x32.png"><link rel="icon" type="image/png" sizes="16x16" href="https://jetbrains.com/favicon-16x16.png"><meta name="msapplication-TileImage" content="https://resources.jetbrains.com/storage/ui/favicons/mstile-144x144.png"><meta name="msapplication-square70x70logo" content="https://resources.jetbrains.com/storage/ui/favicons/mstile-70x70.png"><meta name="msapplication-square150x150logo" content="https://resources.jetbrains.com/storage/ui/favicons/mstile-150x150.png"><meta name="msapplication-wide310x150logo" content="https://resources.jetbrains.com/storage/ui/favicons/mstile-310x150.png"><meta name="msapplication-square310x310logo" content="https://resources.jetbrains.com/storage/ui/favicons/mstile-310x310.png"><meta name="image" content=""><!-- Open Graph --><meta property="og:title" content="Record machine start button press webhook | Copenhagen Truck Wash API"><meta property="og:description" content=""><meta property="og:image" content=""><meta property="og:site_name" content="Copenhagen Truck Wash API Help"><meta property="og:type" content="website"><meta property="og:locale" content="en_US"><meta property="og:url" content="writerside-documentation/addbuttonpresspost.html"><!-- End Open Graph --><!-- Twitter Card --><meta name="twitter:card" content="summary_large_image"><meta name="twitter:site" content=""><meta name="twitter:title" content="Record machine start button press webhook | Copenhagen Truck Wash API"><meta name="twitter:description" content=""><meta name="twitter:creator" content=""><meta name="twitter:image:src" content=""><!-- End Twitter Card --><!-- Schema.org WebPage --><script type="application/ld+json">{
"@context": "http://schema.org",
"@type": "WebPage",
"@id": "writerside-documentation/addbuttonpresspost.html#webpage",
"url": "writerside-documentation/addbuttonpresspost.html",
"name": "Record machine start button press webhook | Copenhagen Truck Wash API",
"description": "",
"image": "",
"inLanguage":"en-US"
}</script><!-- End Schema.org --><!-- Schema.org WebSite --><script type="application/ld+json">{
"@type": "WebSite",
"@id": "writerside-documentation/#website",
"url": "writerside-documentation/",
"name": "Copenhagen Truck Wash API Help"
}</script><!-- End Schema.org --></head><body data-id="addButtonPressPost" data-main-title="Record machine start button press webhook" data-article-props="{&quot;seeAlsoStyle&quot;:&quot;links&quot;}" data-template="article" data-breadcrumbs="API-Reference.topic|API Reference///Tag_Plate_Scans.topic|Plate Scans///Tag_Plate_Scans_Page_1.topic|Plate Scans - Page 1 of 1"><div class="wrapper"><main class="panel _main"><header class="panel__header"><div class="container"><h3>Copenhagen Truck Wash API Help</h3><div class="panel-trigger"></div></div></header><section class="panel__content"><div class="container"><article class="article" data-shortcut-switcher="inactive"><h1 data-toc="addButtonPressPost" id="addButtonPressPost.topic">Record machine start button press webhook</h1><p id="z33b4b4_2">This endpoint documentation is generated directly from <code class="code" id="z33b4b4_8">openapi.yaml</code>.</p><section class="chapter"><h2 id="endpoint" data-toc="endpoint">Endpoint</h2><div class="code-block" data-lang="http">POST /relay/button/press/post</div></section><section class="chapter"><h2 id="operation" data-toc="operation">Operation</h2><p id="z33b4b4_10">Operation ID: <code class="code" id="z33b4b4_12">addButtonPressPost</code></p><p id="z33b4b4_11">Record machine start button press webhook</p></section><section class="chapter"><h2 id="authentication" data-toc="authentication">Authentication</h2><p id="z33b4b4_13">Security requirements:</p><div class="table-wrapper"><table class="wide" id="z33b4b4_14"><thead><tr class="ijRowHead" id="z33b4b4_15"><th id="z33b4b4_17"><p>Scheme</p></th><th id="z33b4b4_18"><p>Scopes</p></th></tr></thead><tbody><tr id="z33b4b4_16"><td id="z33b4b4_19"><p>BearerAuth</p></td><td id="z33b4b4_20"><p>-</p></td></tr></tbody></table></div></section><section class="chapter"><h2 id="request-body" data-toc="request-body">Request Body</h2><p id="z33b4b4_21">Required: no.</p><p id="z33b4b4_22">Content type: <code class="code" id="z33b4b4_24">application/json</code></p><div class="code-block" data-lang="json">
{
&quot;properties&quot;: {
&quot;lane_id&quot;: {
&quot;type&quot;: &quot;integer&quot;
},
&quot;reg&quot;: {
&quot;type&quot;: &quot;string&quot;
},
&quot;token&quot;: {
&quot;type&quot;: &quot;string&quot;
}
},
&quot;type&quot;: &quot;object&quot;
}
</div></section><section class="chapter"><h2 id="responses" data-toc="responses">Responses</h2><div class="table-wrapper"><table class="wide" id="z33b4b4_25"><thead><tr class="ijRowHead" id="z33b4b4_28"><th id="z33b4b4_31"><p>Status</p></th><th id="z33b4b4_32"><p>Description</p></th><th id="z33b4b4_33"><p>Content Types</p></th></tr></thead><tbody><tr id="z33b4b4_29"><td id="z33b4b4_34"><p>201</p></td><td id="z33b4b4_35"><p>Button press recorded and linked to a self-serve wash session</p></td><td id="z33b4b4_36"><p>application/json</p></td></tr><tr id="z33b4b4_30"><td id="z33b4b4_37"><p>404</p></td><td id="z33b4b4_38"></td><td id="z33b4b4_39"></td></tr></tbody></table></div><p id="z33b4b4_26">Schema for response <code class="code" id="z33b4b4_40">201</code> (<code class="code" id="z33b4b4_41">application/json</code>):</p><div class="code-block" data-lang="json">
{
&quot;$ref&quot;: &quot;#/components/schemas/MachineButtonPressWebhookResponse&quot;
}
</div></section><div class="last-modified">17 March 2026</div><div data-feedback-placeholder="true"></div><div class="navigation-links _bottom"><a href="addbuttonpress.html" class="navigation-links__prev">Record machine start button press webhook</a><a href="tag-config.html" class="navigation-links__next">Config</a></div></article><div id="disqus_thread"></div></div></section></main></div><script src="https://resources.jetbrains.com/writerside/apidoc/6.26.0-b913/app.js"></script></body></html>
@@ -0,0 +1,20 @@
<!DOCTYPE html SYSTEM "about:legacy-compat">
<html lang="en-US" data-preset="contrast" data-primary-color="#307FFF"><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8"><meta charset="UTF-8"><meta name="robots" content="noindex"><meta name="built-on" content="2026-03-17T15:40:06.10673471"><title>Add customer attribute | Copenhagen Truck Wash API</title><script type="application/json" id="virtual-toc-data">[{"id":"endpoint","level":0,"title":"Endpoint","anchor":"#endpoint"},{"id":"operation","level":0,"title":"Operation","anchor":"#operation"},{"id":"authentication","level":0,"title":"Authentication","anchor":"#authentication"},{"id":"request-body","level":0,"title":"Request Body","anchor":"#request-body"},{"id":"responses","level":0,"title":"Responses","anchor":"#responses"}]</script><script type="application/json" id="topic-shortcuts"></script><link href="https://resources.jetbrains.com/writerside/apidoc/6.26.0-b913/app.css" rel="stylesheet"><meta name="msapplication-TileColor" content="#000000"><link rel="apple-touch-icon" sizes="180x180" href="https://jetbrains.com/apple-touch-icon.png"><link rel="icon" type="image/png" sizes="32x32" href="https://jetbrains.com/favicon-32x32.png"><link rel="icon" type="image/png" sizes="16x16" href="https://jetbrains.com/favicon-16x16.png"><meta name="msapplication-TileImage" content="https://resources.jetbrains.com/storage/ui/favicons/mstile-144x144.png"><meta name="msapplication-square70x70logo" content="https://resources.jetbrains.com/storage/ui/favicons/mstile-70x70.png"><meta name="msapplication-square150x150logo" content="https://resources.jetbrains.com/storage/ui/favicons/mstile-150x150.png"><meta name="msapplication-wide310x150logo" content="https://resources.jetbrains.com/storage/ui/favicons/mstile-310x150.png"><meta name="msapplication-square310x310logo" content="https://resources.jetbrains.com/storage/ui/favicons/mstile-310x310.png"><meta name="image" content=""><!-- Open Graph --><meta property="og:title" content="Add customer attribute | Copenhagen Truck Wash API"><meta property="og:description" content=""><meta property="og:image" content=""><meta property="og:site_name" content="Copenhagen Truck Wash API Help"><meta property="og:type" content="website"><meta property="og:locale" content="en_US"><meta property="og:url" content="writerside-documentation/addcustomerattribute.html"><!-- End Open Graph --><!-- Twitter Card --><meta name="twitter:card" content="summary_large_image"><meta name="twitter:site" content=""><meta name="twitter:title" content="Add customer attribute | Copenhagen Truck Wash API"><meta name="twitter:description" content=""><meta name="twitter:creator" content=""><meta name="twitter:image:src" content=""><!-- End Twitter Card --><!-- Schema.org WebPage --><script type="application/ld+json">{
"@context": "http://schema.org",
"@type": "WebPage",
"@id": "writerside-documentation/addcustomerattribute.html#webpage",
"url": "writerside-documentation/addcustomerattribute.html",
"name": "Add customer attribute | Copenhagen Truck Wash API",
"description": "",
"image": "",
"inLanguage":"en-US"
}</script><!-- End Schema.org --><!-- Schema.org WebSite --><script type="application/ld+json">{
"@type": "WebSite",
"@id": "writerside-documentation/#website",
"url": "writerside-documentation/",
"name": "Copenhagen Truck Wash API Help"
}</script><!-- End Schema.org --></head><body data-id="addCustomerAttribute" data-main-title="Add customer attribute" data-article-props="{&quot;seeAlsoStyle&quot;:&quot;links&quot;}" data-template="article" data-breadcrumbs="API-Reference.topic|API Reference///Tag_Users.topic|Users///Tag_Users_Page_1.topic|Users - Page 1 of 2"><div class="wrapper"><main class="panel _main"><header class="panel__header"><div class="container"><h3>Copenhagen Truck Wash API Help</h3><div class="panel-trigger"></div></div></header><section class="panel__content"><div class="container"><article class="article" data-shortcut-switcher="inactive"><h1 data-toc="addCustomerAttribute" id="addCustomerAttribute.topic">Add customer attribute</h1><p id="-9vye9v_2">This endpoint documentation is generated directly from <code class="code" id="-9vye9v_8">openapi.yaml</code>.</p><section class="chapter"><h2 id="endpoint" data-toc="endpoint">Endpoint</h2><div class="code-block" data-lang="http">POST /customer/attributes</div></section><section class="chapter"><h2 id="operation" data-toc="operation">Operation</h2><p id="-9vye9v_10">Operation ID: <code class="code" id="-9vye9v_12">addCustomerAttribute</code></p><p id="-9vye9v_11">Add a custom attribute to a customer</p></section><section class="chapter"><h2 id="authentication" data-toc="authentication">Authentication</h2><p id="-9vye9v_13">Security requirements:</p><div class="table-wrapper"><table class="wide" id="-9vye9v_14"><thead><tr class="ijRowHead" id="-9vye9v_15"><th id="-9vye9v_17"><p>Scheme</p></th><th id="-9vye9v_18"><p>Scopes</p></th></tr></thead><tbody><tr id="-9vye9v_16"><td id="-9vye9v_19"><p>BearerAuth</p></td><td id="-9vye9v_20"><p>-</p></td></tr></tbody></table></div></section><section class="chapter"><h2 id="request-body" data-toc="request-body">Request Body</h2><p id="-9vye9v_21">Required: no.</p><p id="-9vye9v_22">Content type: <code class="code" id="-9vye9v_24">application/json</code></p><div class="code-block" data-lang="json">
{}
</div></section><section class="chapter"><h2 id="responses" data-toc="responses">Responses</h2><div class="table-wrapper"><table class="wide" id="-9vye9v_25"><thead><tr class="ijRowHead" id="-9vye9v_28"><th id="-9vye9v_30"><p>Status</p></th><th id="-9vye9v_31"><p>Description</p></th><th id="-9vye9v_32"><p>Content Types</p></th></tr></thead><tbody><tr id="-9vye9v_29"><td id="-9vye9v_33"><p>201</p></td><td id="-9vye9v_34"><p>Customer attribute added successfully</p></td><td id="-9vye9v_35"><p>application/json</p></td></tr></tbody></table></div><p id="-9vye9v_26">Schema for response <code class="code" id="-9vye9v_36">201</code> (<code class="code" id="-9vye9v_37">application/json</code>):</p><div class="code-block" data-lang="json">
{}
</div></section><div class="last-modified">17 March 2026</div><div data-feedback-placeholder="true"></div><div class="navigation-links _bottom"><a href="getcustomerattributes.html" class="navigation-links__prev">Get customer attributes</a><a href="deletecustomerdefaultdepartment.html" class="navigation-links__next">Delete customer default department</a></div></article><div id="disqus_thread"></div></div></section></main></div><script src="https://resources.jetbrains.com/writerside/apidoc/6.26.0-b913/app.js"></script></body></html>
@@ -0,0 +1,33 @@
<!DOCTYPE html SYSTEM "about:legacy-compat">
<html lang="en-US" data-preset="contrast" data-primary-color="#307FFF"><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8"><meta charset="UTF-8"><meta name="robots" content="noindex"><meta name="built-on" content="2026-03-17T15:38:45.856148375"><title>Add customer code | Copenhagen Truck Wash API</title><script type="application/json" id="virtual-toc-data">[{"id":"endpoint","level":0,"title":"Endpoint","anchor":"#endpoint"},{"id":"operation","level":0,"title":"Operation","anchor":"#operation"},{"id":"authentication","level":0,"title":"Authentication","anchor":"#authentication"},{"id":"request-body","level":0,"title":"Request Body","anchor":"#request-body"},{"id":"responses","level":0,"title":"Responses","anchor":"#responses"}]</script><script type="application/json" id="topic-shortcuts"></script><link href="https://resources.jetbrains.com/writerside/apidoc/6.26.0-b913/app.css" rel="stylesheet"><meta name="msapplication-TileColor" content="#000000"><link rel="apple-touch-icon" sizes="180x180" href="https://jetbrains.com/apple-touch-icon.png"><link rel="icon" type="image/png" sizes="32x32" href="https://jetbrains.com/favicon-32x32.png"><link rel="icon" type="image/png" sizes="16x16" href="https://jetbrains.com/favicon-16x16.png"><meta name="msapplication-TileImage" content="https://resources.jetbrains.com/storage/ui/favicons/mstile-144x144.png"><meta name="msapplication-square70x70logo" content="https://resources.jetbrains.com/storage/ui/favicons/mstile-70x70.png"><meta name="msapplication-square150x150logo" content="https://resources.jetbrains.com/storage/ui/favicons/mstile-150x150.png"><meta name="msapplication-wide310x150logo" content="https://resources.jetbrains.com/storage/ui/favicons/mstile-310x150.png"><meta name="msapplication-square310x310logo" content="https://resources.jetbrains.com/storage/ui/favicons/mstile-310x310.png"><meta name="image" content=""><!-- Open Graph --><meta property="og:title" content="Add customer code | Copenhagen Truck Wash API"><meta property="og:description" content=""><meta property="og:image" content=""><meta property="og:site_name" content="Copenhagen Truck Wash API Help"><meta property="og:type" content="website"><meta property="og:locale" content="en_US"><meta property="og:url" content="writerside-documentation/addcustomercode.html"><!-- End Open Graph --><!-- Twitter Card --><meta name="twitter:card" content="summary_large_image"><meta name="twitter:site" content=""><meta name="twitter:title" content="Add customer code | Copenhagen Truck Wash API"><meta name="twitter:description" content=""><meta name="twitter:creator" content=""><meta name="twitter:image:src" content=""><!-- End Twitter Card --><!-- Schema.org WebPage --><script type="application/ld+json">{
"@context": "http://schema.org",
"@type": "WebPage",
"@id": "writerside-documentation/addcustomercode.html#webpage",
"url": "writerside-documentation/addcustomercode.html",
"name": "Add customer code | Copenhagen Truck Wash API",
"description": "",
"image": "",
"inLanguage":"en-US"
}</script><!-- End Schema.org --><!-- Schema.org WebSite --><script type="application/ld+json">{
"@type": "WebSite",
"@id": "writerside-documentation/#website",
"url": "writerside-documentation/",
"name": "Copenhagen Truck Wash API Help"
}</script><!-- End Schema.org --></head><body data-id="addCustomerCode" data-main-title="Add customer code" data-article-props="{&quot;seeAlsoStyle&quot;:&quot;links&quot;}" data-template="article" data-breadcrumbs="API-Reference.topic|API Reference///Tag_Users.topic|Users///Tag_Users_Page_1.topic|Users - Page 1 of 2"><div class="wrapper"><main class="panel _main"><header class="panel__header"><div class="container"><h3>Copenhagen Truck Wash API Help</h3><div class="panel-trigger"></div></div></header><section class="panel__content"><div class="container"><article class="article" data-shortcut-switcher="inactive"><h1 data-toc="addCustomerCode" id="addCustomerCode.topic">Add customer code</h1><p id="xxk3ri_2">This endpoint documentation is generated directly from <code class="code" id="xxk3ri_8">openapi.yaml</code>.</p><section class="chapter"><h2 id="endpoint" data-toc="endpoint">Endpoint</h2><div class="code-block" data-lang="http">POST /admin/customer/code</div></section><section class="chapter"><h2 id="operation" data-toc="operation">Operation</h2><p id="xxk3ri_10">Operation ID: <code class="code" id="xxk3ri_12">addCustomerCode</code></p><p id="xxk3ri_11">Add customer code</p></section><section class="chapter"><h2 id="authentication" data-toc="authentication">Authentication</h2><p id="xxk3ri_13">Security requirements:</p><div class="table-wrapper"><table class="wide" id="xxk3ri_14"><thead><tr class="ijRowHead" id="xxk3ri_15"><th id="xxk3ri_17"><p>Scheme</p></th><th id="xxk3ri_18"><p>Scopes</p></th></tr></thead><tbody><tr id="xxk3ri_16"><td id="xxk3ri_19"><p>BearerAuth</p></td><td id="xxk3ri_20"><p>-</p></td></tr></tbody></table></div></section><section class="chapter"><h2 id="request-body" data-toc="request-body">Request Body</h2><p id="xxk3ri_21">Required: yes.</p><p id="xxk3ri_22">Content type: <code class="code" id="xxk3ri_24">application/json</code></p><div class="code-block" data-lang="json">
{
&quot;properties&quot;: {
&quot;code&quot;: {
&quot;type&quot;: &quot;string&quot;
},
&quot;customer_number&quot;: {
&quot;type&quot;: &quot;integer&quot;
},
&quot;user_id&quot;: {
&quot;type&quot;: &quot;integer&quot;
}
},
&quot;type&quot;: &quot;object&quot;
}
</div></section><section class="chapter"><h2 id="responses" data-toc="responses">Responses</h2><div class="table-wrapper"><table class="wide" id="xxk3ri_25"><thead><tr class="ijRowHead" id="xxk3ri_28"><th id="xxk3ri_30"><p>Status</p></th><th id="xxk3ri_31"><p>Description</p></th><th id="xxk3ri_32"><p>Content Types</p></th></tr></thead><tbody><tr id="xxk3ri_29"><td id="xxk3ri_33"><p>200</p></td><td id="xxk3ri_34"><p>Success</p></td><td id="xxk3ri_35"><p>application/json</p></td></tr></tbody></table></div><p id="xxk3ri_26">Schema for response <code class="code" id="xxk3ri_36">200</code> (<code class="code" id="xxk3ri_37">application/json</code>):</p><div class="code-block" data-lang="json">
{}
</div></section><div class="last-modified">17 March 2026</div><div data-feedback-placeholder="true"></div><div class="navigation-links _bottom"><a href="getcustomercode.html" class="navigation-links__prev">Get customer code</a><a href="getuseridfromcustomernumber.html" class="navigation-links__next">Get user ID from customer number</a></div></article><div id="disqus_thread"></div></div></section></main></div><script src="https://resources.jetbrains.com/writerside/apidoc/6.26.0-b913/app.js"></script></body></html>
@@ -0,0 +1,33 @@
<!DOCTYPE html SYSTEM "about:legacy-compat">
<html lang="en-US" data-preset="contrast" data-primary-color="#307FFF"><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8"><meta charset="UTF-8"><meta name="robots" content="noindex"><meta name="built-on" content="2026-03-17T15:41:32.815054228"><title>Add customer default department | Copenhagen Truck Wash API</title><script type="application/json" id="virtual-toc-data">[{"id":"endpoint","level":0,"title":"Endpoint","anchor":"#endpoint"},{"id":"operation","level":0,"title":"Operation","anchor":"#operation"},{"id":"authentication","level":0,"title":"Authentication","anchor":"#authentication"},{"id":"request-body","level":0,"title":"Request Body","anchor":"#request-body"},{"id":"responses","level":0,"title":"Responses","anchor":"#responses"}]</script><script type="application/json" id="topic-shortcuts"></script><link href="https://resources.jetbrains.com/writerside/apidoc/6.26.0-b913/app.css" rel="stylesheet"><meta name="msapplication-TileColor" content="#000000"><link rel="apple-touch-icon" sizes="180x180" href="https://jetbrains.com/apple-touch-icon.png"><link rel="icon" type="image/png" sizes="32x32" href="https://jetbrains.com/favicon-32x32.png"><link rel="icon" type="image/png" sizes="16x16" href="https://jetbrains.com/favicon-16x16.png"><meta name="msapplication-TileImage" content="https://resources.jetbrains.com/storage/ui/favicons/mstile-144x144.png"><meta name="msapplication-square70x70logo" content="https://resources.jetbrains.com/storage/ui/favicons/mstile-70x70.png"><meta name="msapplication-square150x150logo" content="https://resources.jetbrains.com/storage/ui/favicons/mstile-150x150.png"><meta name="msapplication-wide310x150logo" content="https://resources.jetbrains.com/storage/ui/favicons/mstile-310x150.png"><meta name="msapplication-square310x310logo" content="https://resources.jetbrains.com/storage/ui/favicons/mstile-310x310.png"><meta name="image" content=""><!-- Open Graph --><meta property="og:title" content="Add customer default department | Copenhagen Truck Wash API"><meta property="og:description" content=""><meta property="og:image" content=""><meta property="og:site_name" content="Copenhagen Truck Wash API Help"><meta property="og:type" content="website"><meta property="og:locale" content="en_US"><meta property="og:url" content="writerside-documentation/addcustomerdefaultdepartment.html"><!-- End Open Graph --><!-- Twitter Card --><meta name="twitter:card" content="summary_large_image"><meta name="twitter:site" content=""><meta name="twitter:title" content="Add customer default department | Copenhagen Truck Wash API"><meta name="twitter:description" content=""><meta name="twitter:creator" content=""><meta name="twitter:image:src" content=""><!-- End Twitter Card --><!-- Schema.org WebPage --><script type="application/ld+json">{
"@context": "http://schema.org",
"@type": "WebPage",
"@id": "writerside-documentation/addcustomerdefaultdepartment.html#webpage",
"url": "writerside-documentation/addcustomerdefaultdepartment.html",
"name": "Add customer default department | Copenhagen Truck Wash API",
"description": "",
"image": "",
"inLanguage":"en-US"
}</script><!-- End Schema.org --><!-- Schema.org WebSite --><script type="application/ld+json">{
"@type": "WebSite",
"@id": "writerside-documentation/#website",
"url": "writerside-documentation/",
"name": "Copenhagen Truck Wash API Help"
}</script><!-- End Schema.org --></head><body data-id="addCustomerDefaultDepartment" data-main-title="Add customer default department" data-article-props="{&quot;seeAlsoStyle&quot;:&quot;links&quot;}" data-template="article" data-breadcrumbs="API-Reference.topic|API Reference///Tag_Users.topic|Users///Tag_Users_Page_1.topic|Users - Page 1 of 2"><div class="wrapper"><main class="panel _main"><header class="panel__header"><div class="container"><h3>Copenhagen Truck Wash API Help</h3><div class="panel-trigger"></div></div></header><section class="panel__content"><div class="container"><article class="article" data-shortcut-switcher="inactive"><h1 data-toc="addCustomerDefaultDepartment" id="addCustomerDefaultDepartment.topic">Add customer default department</h1><p id="z7oi8r8_2">This endpoint documentation is generated directly from <code class="code" id="z7oi8r8_8">openapi.yaml</code>.</p><section class="chapter"><h2 id="endpoint" data-toc="endpoint">Endpoint</h2><div class="code-block" data-lang="http">POST /customer/department/default</div></section><section class="chapter"><h2 id="operation" data-toc="operation">Operation</h2><p id="z7oi8r8_10">Operation ID: <code class="code" id="z7oi8r8_12">addCustomerDefaultDepartment</code></p><p id="z7oi8r8_11">Add customer default department</p></section><section class="chapter"><h2 id="authentication" data-toc="authentication">Authentication</h2><p id="z7oi8r8_13">Security requirements:</p><div class="table-wrapper"><table class="wide" id="z7oi8r8_14"><thead><tr class="ijRowHead" id="z7oi8r8_15"><th id="z7oi8r8_17"><p>Scheme</p></th><th id="z7oi8r8_18"><p>Scopes</p></th></tr></thead><tbody><tr id="z7oi8r8_16"><td id="z7oi8r8_19"><p>BearerAuth</p></td><td id="z7oi8r8_20"><p>-</p></td></tr></tbody></table></div></section><section class="chapter"><h2 id="request-body" data-toc="request-body">Request Body</h2><p id="z7oi8r8_21">Required: yes.</p><p id="z7oi8r8_22">Content type: <code class="code" id="z7oi8r8_24">application/json</code></p><div class="code-block" data-lang="json">
{
&quot;properties&quot;: {
&quot;customer_number&quot;: {
&quot;type&quot;: &quot;integer&quot;
},
&quot;department&quot;: {
&quot;type&quot;: &quot;integer&quot;
}
},
&quot;required&quot;: [
&quot;department&quot;
],
&quot;type&quot;: &quot;object&quot;
}
</div></section><section class="chapter"><h2 id="responses" data-toc="responses">Responses</h2><div class="table-wrapper"><table class="wide" id="z7oi8r8_25"><thead><tr class="ijRowHead" id="z7oi8r8_28"><th id="z7oi8r8_30"><p>Status</p></th><th id="z7oi8r8_31"><p>Description</p></th><th id="z7oi8r8_32"><p>Content Types</p></th></tr></thead><tbody><tr id="z7oi8r8_29"><td id="z7oi8r8_33"><p>200</p></td><td id="z7oi8r8_34"><p>Success</p></td><td id="z7oi8r8_35"><p>application/json</p></td></tr></tbody></table></div><p id="z7oi8r8_26">Schema for response <code class="code" id="z7oi8r8_36">200</code> (<code class="code" id="z7oi8r8_37">application/json</code>):</p><div class="code-block" data-lang="json">
{}
</div></section><div class="last-modified">17 March 2026</div><div data-feedback-placeholder="true"></div><div class="navigation-links _bottom"><a href="getcustomerdefaultdepartment.html" class="navigation-links__prev">Get customer default department</a><a href="deletecustomernote.html" class="navigation-links__next">Delete customer note</a></div></article><div id="disqus_thread"></div></div></section></main></div><script src="https://resources.jetbrains.com/writerside/apidoc/6.26.0-b913/app.js"></script></body></html>
@@ -0,0 +1,38 @@
<!DOCTYPE html SYSTEM "about:legacy-compat">
<html lang="en-US" data-preset="contrast" data-primary-color="#307FFF"><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8"><meta charset="UTF-8"><meta name="robots" content="noindex"><meta name="built-on" content="2026-03-17T15:43:01.514331845"><title>Add customer fixed pricing | Copenhagen Truck Wash API</title><script type="application/json" id="virtual-toc-data">[{"id":"endpoint","level":0,"title":"Endpoint","anchor":"#endpoint"},{"id":"operation","level":0,"title":"Operation","anchor":"#operation"},{"id":"authentication","level":0,"title":"Authentication","anchor":"#authentication"},{"id":"request-body","level":0,"title":"Request Body","anchor":"#request-body"},{"id":"responses","level":0,"title":"Responses","anchor":"#responses"}]</script><script type="application/json" id="topic-shortcuts"></script><link href="https://resources.jetbrains.com/writerside/apidoc/6.26.0-b913/app.css" rel="stylesheet"><meta name="msapplication-TileColor" content="#000000"><link rel="apple-touch-icon" sizes="180x180" href="https://jetbrains.com/apple-touch-icon.png"><link rel="icon" type="image/png" sizes="32x32" href="https://jetbrains.com/favicon-32x32.png"><link rel="icon" type="image/png" sizes="16x16" href="https://jetbrains.com/favicon-16x16.png"><meta name="msapplication-TileImage" content="https://resources.jetbrains.com/storage/ui/favicons/mstile-144x144.png"><meta name="msapplication-square70x70logo" content="https://resources.jetbrains.com/storage/ui/favicons/mstile-70x70.png"><meta name="msapplication-square150x150logo" content="https://resources.jetbrains.com/storage/ui/favicons/mstile-150x150.png"><meta name="msapplication-wide310x150logo" content="https://resources.jetbrains.com/storage/ui/favicons/mstile-310x150.png"><meta name="msapplication-square310x310logo" content="https://resources.jetbrains.com/storage/ui/favicons/mstile-310x310.png"><meta name="image" content=""><!-- Open Graph --><meta property="og:title" content="Add customer fixed pricing | Copenhagen Truck Wash API"><meta property="og:description" content=""><meta property="og:image" content=""><meta property="og:site_name" content="Copenhagen Truck Wash API Help"><meta property="og:type" content="website"><meta property="og:locale" content="en_US"><meta property="og:url" content="writerside-documentation/addcustomerfixedpricing.html"><!-- End Open Graph --><!-- Twitter Card --><meta name="twitter:card" content="summary_large_image"><meta name="twitter:site" content=""><meta name="twitter:title" content="Add customer fixed pricing | Copenhagen Truck Wash API"><meta name="twitter:description" content=""><meta name="twitter:creator" content=""><meta name="twitter:image:src" content=""><!-- End Twitter Card --><!-- Schema.org WebPage --><script type="application/ld+json">{
"@context": "http://schema.org",
"@type": "WebPage",
"@id": "writerside-documentation/addcustomerfixedpricing.html#webpage",
"url": "writerside-documentation/addcustomerfixedpricing.html",
"name": "Add customer fixed pricing | Copenhagen Truck Wash API",
"description": "",
"image": "",
"inLanguage":"en-US"
}</script><!-- End Schema.org --><!-- Schema.org WebSite --><script type="application/ld+json">{
"@type": "WebSite",
"@id": "writerside-documentation/#website",
"url": "writerside-documentation/",
"name": "Copenhagen Truck Wash API Help"
}</script><!-- End Schema.org --></head><body data-id="addCustomerFixedPricing" data-main-title="Add customer fixed pricing" data-article-props="{&quot;seeAlsoStyle&quot;:&quot;links&quot;}" data-template="article" data-breadcrumbs="API-Reference.topic|API Reference///Tag_Users.topic|Users///Tag_Users_Page_1.topic|Users - Page 1 of 2"><div class="wrapper"><main class="panel _main"><header class="panel__header"><div class="container"><h3>Copenhagen Truck Wash API Help</h3><div class="panel-trigger"></div></div></header><section class="panel__content"><div class="container"><article class="article" data-shortcut-switcher="inactive"><h1 data-toc="addCustomerFixedPricing" id="addCustomerFixedPricing.topic">Add customer fixed pricing</h1><p id="upkh77_2">This endpoint documentation is generated directly from <code class="code" id="upkh77_8">openapi.yaml</code>.</p><section class="chapter"><h2 id="endpoint" data-toc="endpoint">Endpoint</h2><div class="code-block" data-lang="http">POST /customer/pricing/fixed</div></section><section class="chapter"><h2 id="operation" data-toc="operation">Operation</h2><p id="upkh77_10">Operation ID: <code class="code" id="upkh77_12">addCustomerFixedPricing</code></p><p id="upkh77_11">Add customer fixed pricing</p></section><section class="chapter"><h2 id="authentication" data-toc="authentication">Authentication</h2><p id="upkh77_13">Security requirements:</p><div class="table-wrapper"><table class="wide" id="upkh77_14"><thead><tr class="ijRowHead" id="upkh77_15"><th id="upkh77_17"><p>Scheme</p></th><th id="upkh77_18"><p>Scopes</p></th></tr></thead><tbody><tr id="upkh77_16"><td id="upkh77_19"><p>BearerAuth</p></td><td id="upkh77_20"><p>-</p></td></tr></tbody></table></div></section><section class="chapter"><h2 id="request-body" data-toc="request-body">Request Body</h2><p id="upkh77_21">Required: yes.</p><p id="upkh77_22">Content type: <code class="code" id="upkh77_24">application/json</code></p><div class="code-block" data-lang="json">
{
&quot;properties&quot;: {
&quot;customer_number&quot;: {
&quot;type&quot;: &quot;integer&quot;
},
&quot;description&quot;: {
&quot;type&quot;: &quot;string&quot;
},
&quot;price&quot;: {
&quot;type&quot;: &quot;integer&quot;
}
},
&quot;required&quot;: [
&quot;customer_number&quot;,
&quot;price&quot;,
&quot;description&quot;
],
&quot;type&quot;: &quot;object&quot;
}
</div></section><section class="chapter"><h2 id="responses" data-toc="responses">Responses</h2><div class="table-wrapper"><table class="wide" id="upkh77_25"><thead><tr class="ijRowHead" id="upkh77_28"><th id="upkh77_30"><p>Status</p></th><th id="upkh77_31"><p>Description</p></th><th id="upkh77_32"><p>Content Types</p></th></tr></thead><tbody><tr id="upkh77_29"><td id="upkh77_33"><p>200</p></td><td id="upkh77_34"><p>Success</p></td><td id="upkh77_35"><p>application/json</p></td></tr></tbody></table></div><p id="upkh77_26">Schema for response <code class="code" id="upkh77_36">200</code> (<code class="code" id="upkh77_37">application/json</code>):</p><div class="code-block" data-lang="json">
{}
</div></section><div class="last-modified">17 March 2026</div><div data-feedback-placeholder="true"></div><div class="navigation-links _bottom"><a href="getcustomerfixedpricing.html" class="navigation-links__prev">Get customer fixed pricing</a><a href="listcustomers.html" class="navigation-links__next">List customers</a></div></article><div id="disqus_thread"></div></div></section></main></div><script src="https://resources.jetbrains.com/writerside/apidoc/6.26.0-b913/app.js"></script></body></html>
@@ -0,0 +1,20 @@
<!DOCTYPE html SYSTEM "about:legacy-compat">
<html lang="en-US" data-preset="contrast" data-primary-color="#307FFF"><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8"><meta charset="UTF-8"><meta name="robots" content="noindex"><meta name="built-on" content="2026-03-17T15:42:10.315625351"><title>Add customer note | Copenhagen Truck Wash API</title><script type="application/json" id="virtual-toc-data">[{"id":"endpoint","level":0,"title":"Endpoint","anchor":"#endpoint"},{"id":"operation","level":0,"title":"Operation","anchor":"#operation"},{"id":"authentication","level":0,"title":"Authentication","anchor":"#authentication"},{"id":"request-body","level":0,"title":"Request Body","anchor":"#request-body"},{"id":"responses","level":0,"title":"Responses","anchor":"#responses"}]</script><script type="application/json" id="topic-shortcuts"></script><link href="https://resources.jetbrains.com/writerside/apidoc/6.26.0-b913/app.css" rel="stylesheet"><meta name="msapplication-TileColor" content="#000000"><link rel="apple-touch-icon" sizes="180x180" href="https://jetbrains.com/apple-touch-icon.png"><link rel="icon" type="image/png" sizes="32x32" href="https://jetbrains.com/favicon-32x32.png"><link rel="icon" type="image/png" sizes="16x16" href="https://jetbrains.com/favicon-16x16.png"><meta name="msapplication-TileImage" content="https://resources.jetbrains.com/storage/ui/favicons/mstile-144x144.png"><meta name="msapplication-square70x70logo" content="https://resources.jetbrains.com/storage/ui/favicons/mstile-70x70.png"><meta name="msapplication-square150x150logo" content="https://resources.jetbrains.com/storage/ui/favicons/mstile-150x150.png"><meta name="msapplication-wide310x150logo" content="https://resources.jetbrains.com/storage/ui/favicons/mstile-310x150.png"><meta name="msapplication-square310x310logo" content="https://resources.jetbrains.com/storage/ui/favicons/mstile-310x310.png"><meta name="image" content=""><!-- Open Graph --><meta property="og:title" content="Add customer note | Copenhagen Truck Wash API"><meta property="og:description" content=""><meta property="og:image" content=""><meta property="og:site_name" content="Copenhagen Truck Wash API Help"><meta property="og:type" content="website"><meta property="og:locale" content="en_US"><meta property="og:url" content="writerside-documentation/addcustomernote.html"><!-- End Open Graph --><!-- Twitter Card --><meta name="twitter:card" content="summary_large_image"><meta name="twitter:site" content=""><meta name="twitter:title" content="Add customer note | Copenhagen Truck Wash API"><meta name="twitter:description" content=""><meta name="twitter:creator" content=""><meta name="twitter:image:src" content=""><!-- End Twitter Card --><!-- Schema.org WebPage --><script type="application/ld+json">{
"@context": "http://schema.org",
"@type": "WebPage",
"@id": "writerside-documentation/addcustomernote.html#webpage",
"url": "writerside-documentation/addcustomernote.html",
"name": "Add customer note | Copenhagen Truck Wash API",
"description": "",
"image": "",
"inLanguage":"en-US"
}</script><!-- End Schema.org --><!-- Schema.org WebSite --><script type="application/ld+json">{
"@type": "WebSite",
"@id": "writerside-documentation/#website",
"url": "writerside-documentation/",
"name": "Copenhagen Truck Wash API Help"
}</script><!-- End Schema.org --></head><body data-id="addCustomerNote" data-main-title="Add customer note" data-article-props="{&quot;seeAlsoStyle&quot;:&quot;links&quot;}" data-template="article" data-breadcrumbs="API-Reference.topic|API Reference///Tag_Users.topic|Users///Tag_Users_Page_1.topic|Users - Page 1 of 2"><div class="wrapper"><main class="panel _main"><header class="panel__header"><div class="container"><h3>Copenhagen Truck Wash API Help</h3><div class="panel-trigger"></div></div></header><section class="panel__content"><div class="container"><article class="article" data-shortcut-switcher="inactive"><h1 data-toc="addCustomerNote" id="addCustomerNote.topic">Add customer note</h1><p id="x5vq6r_2">This endpoint documentation is generated directly from <code class="code" id="x5vq6r_8">openapi.yaml</code>.</p><section class="chapter"><h2 id="endpoint" data-toc="endpoint">Endpoint</h2><div class="code-block" data-lang="http">POST /customer/notes</div></section><section class="chapter"><h2 id="operation" data-toc="operation">Operation</h2><p id="x5vq6r_10">Operation ID: <code class="code" id="x5vq6r_12">addCustomerNote</code></p><p id="x5vq6r_11">Add a note to a customer</p></section><section class="chapter"><h2 id="authentication" data-toc="authentication">Authentication</h2><p id="x5vq6r_13">Security requirements:</p><div class="table-wrapper"><table class="wide" id="x5vq6r_14"><thead><tr class="ijRowHead" id="x5vq6r_15"><th id="x5vq6r_17"><p>Scheme</p></th><th id="x5vq6r_18"><p>Scopes</p></th></tr></thead><tbody><tr id="x5vq6r_16"><td id="x5vq6r_19"><p>BearerAuth</p></td><td id="x5vq6r_20"><p>-</p></td></tr></tbody></table></div></section><section class="chapter"><h2 id="request-body" data-toc="request-body">Request Body</h2><p id="x5vq6r_21">Required: no.</p><p id="x5vq6r_22">Content type: <code class="code" id="x5vq6r_24">application/json</code></p><div class="code-block" data-lang="json">
{}
</div></section><section class="chapter"><h2 id="responses" data-toc="responses">Responses</h2><div class="table-wrapper"><table class="wide" id="x5vq6r_25"><thead><tr class="ijRowHead" id="x5vq6r_28"><th id="x5vq6r_30"><p>Status</p></th><th id="x5vq6r_31"><p>Description</p></th><th id="x5vq6r_32"><p>Content Types</p></th></tr></thead><tbody><tr id="x5vq6r_29"><td id="x5vq6r_33"><p>201</p></td><td id="x5vq6r_34"><p>Customer note added successfully</p></td><td id="x5vq6r_35"><p>application/json</p></td></tr></tbody></table></div><p id="x5vq6r_26">Schema for response <code class="code" id="x5vq6r_36">201</code> (<code class="code" id="x5vq6r_37">application/json</code>):</p><div class="code-block" data-lang="json">
{}
</div></section><div class="last-modified">17 March 2026</div><div data-feedback-placeholder="true"></div><div class="navigation-links _bottom"><a href="getcustomernotes.html" class="navigation-links__prev">Get customer notes</a><a href="deletecustomerfixedpricing.html" class="navigation-links__next">Delete customer fixed pricing</a></div></article><div id="disqus_thread"></div></div></section></main></div><script src="https://resources.jetbrains.com/writerside/apidoc/6.26.0-b913/app.js"></script></body></html>
@@ -0,0 +1,38 @@
<!DOCTYPE html SYSTEM "about:legacy-compat">
<html lang="en-US" data-preset="contrast" data-primary-color="#307FFF"><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8"><meta charset="UTF-8"><meta name="robots" content="noindex"><meta name="built-on" content="2026-03-17T15:40:57.560621286"><title>Add daily report | Copenhagen Truck Wash API</title><script type="application/json" id="virtual-toc-data">[{"id":"endpoint","level":0,"title":"Endpoint","anchor":"#endpoint"},{"id":"operation","level":0,"title":"Operation","anchor":"#operation"},{"id":"authentication","level":0,"title":"Authentication","anchor":"#authentication"},{"id":"request-body","level":0,"title":"Request Body","anchor":"#request-body"},{"id":"responses","level":0,"title":"Responses","anchor":"#responses"}]</script><script type="application/json" id="topic-shortcuts"></script><link href="https://resources.jetbrains.com/writerside/apidoc/6.26.0-b913/app.css" rel="stylesheet"><meta name="msapplication-TileColor" content="#000000"><link rel="apple-touch-icon" sizes="180x180" href="https://jetbrains.com/apple-touch-icon.png"><link rel="icon" type="image/png" sizes="32x32" href="https://jetbrains.com/favicon-32x32.png"><link rel="icon" type="image/png" sizes="16x16" href="https://jetbrains.com/favicon-16x16.png"><meta name="msapplication-TileImage" content="https://resources.jetbrains.com/storage/ui/favicons/mstile-144x144.png"><meta name="msapplication-square70x70logo" content="https://resources.jetbrains.com/storage/ui/favicons/mstile-70x70.png"><meta name="msapplication-square150x150logo" content="https://resources.jetbrains.com/storage/ui/favicons/mstile-150x150.png"><meta name="msapplication-wide310x150logo" content="https://resources.jetbrains.com/storage/ui/favicons/mstile-310x150.png"><meta name="msapplication-square310x310logo" content="https://resources.jetbrains.com/storage/ui/favicons/mstile-310x310.png"><meta name="image" content=""><!-- Open Graph --><meta property="og:title" content="Add daily report | Copenhagen Truck Wash API"><meta property="og:description" content=""><meta property="og:image" content=""><meta property="og:site_name" content="Copenhagen Truck Wash API Help"><meta property="og:type" content="website"><meta property="og:locale" content="en_US"><meta property="og:url" content="writerside-documentation/adddailyreport.html"><!-- End Open Graph --><!-- Twitter Card --><meta name="twitter:card" content="summary_large_image"><meta name="twitter:site" content=""><meta name="twitter:title" content="Add daily report | Copenhagen Truck Wash API"><meta name="twitter:description" content=""><meta name="twitter:creator" content=""><meta name="twitter:image:src" content=""><!-- End Twitter Card --><!-- Schema.org WebPage --><script type="application/ld+json">{
"@context": "http://schema.org",
"@type": "WebPage",
"@id": "writerside-documentation/adddailyreport.html#webpage",
"url": "writerside-documentation/adddailyreport.html",
"name": "Add daily report | Copenhagen Truck Wash API",
"description": "",
"image": "",
"inLanguage":"en-US"
}</script><!-- End Schema.org --><!-- Schema.org WebSite --><script type="application/ld+json">{
"@type": "WebSite",
"@id": "writerside-documentation/#website",
"url": "writerside-documentation/",
"name": "Copenhagen Truck Wash API Help"
}</script><!-- End Schema.org --></head><body data-id="addDailyReport" data-main-title="Add daily report" data-article-props="{&quot;seeAlsoStyle&quot;:&quot;links&quot;}" data-template="article" data-breadcrumbs="API-Reference.topic|API Reference///Tag_Departments.topic|Departments///Tag_Departments_Page_1.topic|Departments - Page 1 of 2"><div class="wrapper"><main class="panel _main"><header class="panel__header"><div class="container"><h3>Copenhagen Truck Wash API Help</h3><div class="panel-trigger"></div></div></header><section class="panel__content"><div class="container"><article class="article" data-shortcut-switcher="inactive"><h1 data-toc="addDailyReport" id="addDailyReport.topic">Add daily report</h1><p id="weug98_2">This endpoint documentation is generated directly from <code class="code" id="weug98_8">openapi.yaml</code>.</p><section class="chapter"><h2 id="endpoint" data-toc="endpoint">Endpoint</h2><div class="code-block" data-lang="http">POST /departments/daily-reports</div></section><section class="chapter"><h2 id="operation" data-toc="operation">Operation</h2><p id="weug98_10">Operation ID: <code class="code" id="weug98_12">addDailyReport</code></p><p id="weug98_11">Add daily report</p></section><section class="chapter"><h2 id="authentication" data-toc="authentication">Authentication</h2><p id="weug98_13">Security requirements:</p><div class="table-wrapper"><table class="wide" id="weug98_14"><thead><tr class="ijRowHead" id="weug98_15"><th id="weug98_17"><p>Scheme</p></th><th id="weug98_18"><p>Scopes</p></th></tr></thead><tbody><tr id="weug98_16"><td id="weug98_19"><p>BearerAuth</p></td><td id="weug98_20"><p>-</p></td></tr></tbody></table></div></section><section class="chapter"><h2 id="request-body" data-toc="request-body">Request Body</h2><p id="weug98_21">Required: yes.</p><p id="weug98_22">Content type: <code class="code" id="weug98_24">application/json</code></p><div class="code-block" data-lang="json">
{
&quot;properties&quot;: {
&quot;date&quot;: {
&quot;type&quot;: &quot;string&quot;
},
&quot;department_id&quot;: {
&quot;type&quot;: &quot;integer&quot;
},
&quot;report&quot;: {
&quot;type&quot;: &quot;string&quot;
}
},
&quot;required&quot;: [
&quot;department_id&quot;,
&quot;date&quot;,
&quot;report&quot;
],
&quot;type&quot;: &quot;object&quot;
}
</div></section><section class="chapter"><h2 id="responses" data-toc="responses">Responses</h2><div class="table-wrapper"><table class="wide" id="weug98_25"><thead><tr class="ijRowHead" id="weug98_28"><th id="weug98_30"><p>Status</p></th><th id="weug98_31"><p>Description</p></th><th id="weug98_32"><p>Content Types</p></th></tr></thead><tbody><tr id="weug98_29"><td id="weug98_33"><p>200</p></td><td id="weug98_34"><p>Success</p></td><td id="weug98_35"><p>application/json</p></td></tr></tbody></table></div><p id="weug98_26">Schema for response <code class="code" id="weug98_36">200</code> (<code class="code" id="weug98_37">application/json</code>):</p><div class="code-block" data-lang="json">
{}
</div></section><div class="last-modified">17 March 2026</div><div data-feedback-placeholder="true"></div><div class="navigation-links _bottom"><a href="listdailyreports.html" class="navigation-links__prev">List daily reports</a><a href="tag-departments-page-2.html" class="navigation-links__next">Departments - Page 2 of 2</a></div></article><div id="disqus_thread"></div></div></section></main></div><script src="https://resources.jetbrains.com/writerside/apidoc/6.26.0-b913/app.js"></script></body></html>
@@ -0,0 +1,30 @@
<!DOCTYPE html SYSTEM "about:legacy-compat">
<html lang="en-US" data-preset="contrast" data-primary-color="#307FFF"><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8"><meta charset="UTF-8"><meta name="robots" content="noindex"><meta name="built-on" content="2026-03-17T15:40:01.515837311"><title>Add category to department | Copenhagen Truck Wash API</title><script type="application/json" id="virtual-toc-data">[{"id":"endpoint","level":0,"title":"Endpoint","anchor":"#endpoint"},{"id":"operation","level":0,"title":"Operation","anchor":"#operation"},{"id":"authentication","level":0,"title":"Authentication","anchor":"#authentication"},{"id":"request-body","level":0,"title":"Request Body","anchor":"#request-body"},{"id":"responses","level":0,"title":"Responses","anchor":"#responses"}]</script><script type="application/json" id="topic-shortcuts"></script><link href="https://resources.jetbrains.com/writerside/apidoc/6.26.0-b913/app.css" rel="stylesheet"><meta name="msapplication-TileColor" content="#000000"><link rel="apple-touch-icon" sizes="180x180" href="https://jetbrains.com/apple-touch-icon.png"><link rel="icon" type="image/png" sizes="32x32" href="https://jetbrains.com/favicon-32x32.png"><link rel="icon" type="image/png" sizes="16x16" href="https://jetbrains.com/favicon-16x16.png"><meta name="msapplication-TileImage" content="https://resources.jetbrains.com/storage/ui/favicons/mstile-144x144.png"><meta name="msapplication-square70x70logo" content="https://resources.jetbrains.com/storage/ui/favicons/mstile-70x70.png"><meta name="msapplication-square150x150logo" content="https://resources.jetbrains.com/storage/ui/favicons/mstile-150x150.png"><meta name="msapplication-wide310x150logo" content="https://resources.jetbrains.com/storage/ui/favicons/mstile-310x150.png"><meta name="msapplication-square310x310logo" content="https://resources.jetbrains.com/storage/ui/favicons/mstile-310x310.png"><meta name="image" content=""><!-- Open Graph --><meta property="og:title" content="Add category to department | Copenhagen Truck Wash API"><meta property="og:description" content=""><meta property="og:image" content=""><meta property="og:site_name" content="Copenhagen Truck Wash API Help"><meta property="og:type" content="website"><meta property="og:locale" content="en_US"><meta property="og:url" content="writerside-documentation/adddepartmentcategory.html"><!-- End Open Graph --><!-- Twitter Card --><meta name="twitter:card" content="summary_large_image"><meta name="twitter:site" content=""><meta name="twitter:title" content="Add category to department | Copenhagen Truck Wash API"><meta name="twitter:description" content=""><meta name="twitter:creator" content=""><meta name="twitter:image:src" content=""><!-- End Twitter Card --><!-- Schema.org WebPage --><script type="application/ld+json">{
"@context": "http://schema.org",
"@type": "WebPage",
"@id": "writerside-documentation/adddepartmentcategory.html#webpage",
"url": "writerside-documentation/adddepartmentcategory.html",
"name": "Add category to department | Copenhagen Truck Wash API",
"description": "",
"image": "",
"inLanguage":"en-US"
}</script><!-- End Schema.org --><!-- Schema.org WebSite --><script type="application/ld+json">{
"@type": "WebSite",
"@id": "writerside-documentation/#website",
"url": "writerside-documentation/",
"name": "Copenhagen Truck Wash API Help"
}</script><!-- End Schema.org --></head><body data-id="addDepartmentCategory" data-main-title="Add category to department" data-article-props="{&quot;seeAlsoStyle&quot;:&quot;links&quot;}" data-template="article" data-breadcrumbs="API-Reference.topic|API Reference///Tag_Departments.topic|Departments///Tag_Departments_Page_1.topic|Departments - Page 1 of 2"><div class="wrapper"><main class="panel _main"><header class="panel__header"><div class="container"><h3>Copenhagen Truck Wash API Help</h3><div class="panel-trigger"></div></div></header><section class="panel__content"><div class="container"><article class="article" data-shortcut-switcher="inactive"><h1 data-toc="addDepartmentCategory" id="addDepartmentCategory.topic">Add category to department</h1><p id="-rhmyel_2">This endpoint documentation is generated directly from <code class="code" id="-rhmyel_8">openapi.yaml</code>.</p><section class="chapter"><h2 id="endpoint" data-toc="endpoint">Endpoint</h2><div class="code-block" data-lang="http">POST /departments/categories</div></section><section class="chapter"><h2 id="operation" data-toc="operation">Operation</h2><p id="-rhmyel_10">Operation ID: <code class="code" id="-rhmyel_12">addDepartmentCategory</code></p><p id="-rhmyel_11">Associate a product category with a department</p></section><section class="chapter"><h2 id="authentication" data-toc="authentication">Authentication</h2><p id="-rhmyel_13">Security requirements:</p><div class="table-wrapper"><table class="wide" id="-rhmyel_14"><thead><tr class="ijRowHead" id="-rhmyel_15"><th id="-rhmyel_17"><p>Scheme</p></th><th id="-rhmyel_18"><p>Scopes</p></th></tr></thead><tbody><tr id="-rhmyel_16"><td id="-rhmyel_19"><p>BearerAuth</p></td><td id="-rhmyel_20"><p>-</p></td></tr></tbody></table></div></section><section class="chapter"><h2 id="request-body" data-toc="request-body">Request Body</h2><p id="-rhmyel_21">Required: yes.</p><p id="-rhmyel_22">Content type: <code class="code" id="-rhmyel_24">application/json</code></p><div class="code-block" data-lang="json">
{
&quot;properties&quot;: {
&quot;category_id&quot;: {
&quot;type&quot;: &quot;integer&quot;
},
&quot;department_id&quot;: {
&quot;type&quot;: &quot;integer&quot;
}
},
&quot;type&quot;: &quot;object&quot;
}
</div></section><section class="chapter"><h2 id="responses" data-toc="responses">Responses</h2><div class="table-wrapper"><table class="wide" id="-rhmyel_25"><thead><tr class="ijRowHead" id="-rhmyel_28"><th id="-rhmyel_30"><p>Status</p></th><th id="-rhmyel_31"><p>Description</p></th><th id="-rhmyel_32"><p>Content Types</p></th></tr></thead><tbody><tr id="-rhmyel_29"><td id="-rhmyel_33"><p>201</p></td><td id="-rhmyel_34"><p>Category added to department successfully</p></td><td id="-rhmyel_35"><p>application/json</p></td></tr></tbody></table></div><p id="-rhmyel_26">Schema for response <code class="code" id="-rhmyel_36">201</code> (<code class="code" id="-rhmyel_37">application/json</code>):</p><div class="code-block" data-lang="json">
{}
</div></section><div class="last-modified">17 March 2026</div><div data-feedback-placeholder="true"></div><div class="navigation-links _bottom"><a href="getdepartmentcategories.html" class="navigation-links__prev">Get department categories</a><a href="listdailyreports.html" class="navigation-links__next">List daily reports</a></div></article><div id="disqus_thread"></div></div></section></main></div><script src="https://resources.jetbrains.com/writerside/apidoc/6.26.0-b913/app.js"></script></body></html>
@@ -0,0 +1,22 @@
<!DOCTYPE html SYSTEM "about:legacy-compat">
<html lang="en-US" data-preset="contrast" data-primary-color="#307FFF"><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8"><meta charset="UTF-8"><meta name="robots" content="noindex"><meta name="built-on" content="2026-03-17T15:38:23.960845957"><title>Add item to order | Copenhagen Truck Wash API</title><script type="application/json" id="virtual-toc-data">[{"id":"endpoint","level":0,"title":"Endpoint","anchor":"#endpoint"},{"id":"operation","level":0,"title":"Operation","anchor":"#operation"},{"id":"authentication","level":0,"title":"Authentication","anchor":"#authentication"},{"id":"request-body","level":0,"title":"Request Body","anchor":"#request-body"},{"id":"responses","level":0,"title":"Responses","anchor":"#responses"}]</script><script type="application/json" id="topic-shortcuts"></script><link href="https://resources.jetbrains.com/writerside/apidoc/6.26.0-b913/app.css" rel="stylesheet"><meta name="msapplication-TileColor" content="#000000"><link rel="apple-touch-icon" sizes="180x180" href="https://jetbrains.com/apple-touch-icon.png"><link rel="icon" type="image/png" sizes="32x32" href="https://jetbrains.com/favicon-32x32.png"><link rel="icon" type="image/png" sizes="16x16" href="https://jetbrains.com/favicon-16x16.png"><meta name="msapplication-TileImage" content="https://resources.jetbrains.com/storage/ui/favicons/mstile-144x144.png"><meta name="msapplication-square70x70logo" content="https://resources.jetbrains.com/storage/ui/favicons/mstile-70x70.png"><meta name="msapplication-square150x150logo" content="https://resources.jetbrains.com/storage/ui/favicons/mstile-150x150.png"><meta name="msapplication-wide310x150logo" content="https://resources.jetbrains.com/storage/ui/favicons/mstile-310x150.png"><meta name="msapplication-square310x310logo" content="https://resources.jetbrains.com/storage/ui/favicons/mstile-310x310.png"><meta name="image" content=""><!-- Open Graph --><meta property="og:title" content="Add item to order | Copenhagen Truck Wash API"><meta property="og:description" content=""><meta property="og:image" content=""><meta property="og:site_name" content="Copenhagen Truck Wash API Help"><meta property="og:type" content="website"><meta property="og:locale" content="en_US"><meta property="og:url" content="writerside-documentation/addorderitem.html"><!-- End Open Graph --><!-- Twitter Card --><meta name="twitter:card" content="summary_large_image"><meta name="twitter:site" content=""><meta name="twitter:title" content="Add item to order | Copenhagen Truck Wash API"><meta name="twitter:description" content=""><meta name="twitter:creator" content=""><meta name="twitter:image:src" content=""><!-- End Twitter Card --><!-- Schema.org WebPage --><script type="application/ld+json">{
"@context": "http://schema.org",
"@type": "WebPage",
"@id": "writerside-documentation/addorderitem.html#webpage",
"url": "writerside-documentation/addorderitem.html",
"name": "Add item to order | Copenhagen Truck Wash API",
"description": "",
"image": "",
"inLanguage":"en-US"
}</script><!-- End Schema.org --><!-- Schema.org WebSite --><script type="application/ld+json">{
"@type": "WebSite",
"@id": "writerside-documentation/#website",
"url": "writerside-documentation/",
"name": "Copenhagen Truck Wash API Help"
}</script><!-- End Schema.org --></head><body data-id="addOrderItem" data-main-title="Add item to order" data-article-props="{&quot;seeAlsoStyle&quot;:&quot;links&quot;}" data-template="article" data-breadcrumbs="API-Reference.topic|API Reference///Tag_Order_Items.topic|Order Items///Tag_Order_Items_Page_1.topic|Order Items - Page 1 of 1"><div class="wrapper"><main class="panel _main"><header class="panel__header"><div class="container"><h3>Copenhagen Truck Wash API Help</h3><div class="panel-trigger"></div></div></header><section class="panel__content"><div class="container"><article class="article" data-shortcut-switcher="inactive"><h1 data-toc="addOrderItem" id="addOrderItem.topic">Add item to order</h1><p id="vkfgv4_2">This endpoint documentation is generated directly from <code class="code" id="vkfgv4_8">openapi.yaml</code>.</p><section class="chapter"><h2 id="endpoint" data-toc="endpoint">Endpoint</h2><div class="code-block" data-lang="http">POST /order/items</div></section><section class="chapter"><h2 id="operation" data-toc="operation">Operation</h2><p id="vkfgv4_10">Operation ID: <code class="code" id="vkfgv4_12">addOrderItem</code></p><p id="vkfgv4_11">Add a new item to an existing order</p></section><section class="chapter"><h2 id="authentication" data-toc="authentication">Authentication</h2><p id="vkfgv4_13">Security requirements:</p><div class="table-wrapper"><table class="wide" id="vkfgv4_14"><thead><tr class="ijRowHead" id="vkfgv4_15"><th id="vkfgv4_17"><p>Scheme</p></th><th id="vkfgv4_18"><p>Scopes</p></th></tr></thead><tbody><tr id="vkfgv4_16"><td id="vkfgv4_19"><p>BearerAuth</p></td><td id="vkfgv4_20"><p>-</p></td></tr></tbody></table></div></section><section class="chapter"><h2 id="request-body" data-toc="request-body">Request Body</h2><p id="vkfgv4_21">Required: yes.</p><p id="vkfgv4_22">Content type: <code class="code" id="vkfgv4_24">application/json</code></p><div class="code-block" data-lang="json">
{
&quot;$ref&quot;: &quot;#/components/schemas/OrderItemCreate&quot;
}
</div></section><section class="chapter"><h2 id="responses" data-toc="responses">Responses</h2><div class="table-wrapper"><table class="wide" id="vkfgv4_25"><thead><tr class="ijRowHead" id="vkfgv4_28"><th id="vkfgv4_31"><p>Status</p></th><th id="vkfgv4_32"><p>Description</p></th><th id="vkfgv4_33"><p>Content Types</p></th></tr></thead><tbody><tr id="vkfgv4_29"><td id="vkfgv4_34"><p>201</p></td><td id="vkfgv4_35"><p>Order item added successfully</p></td><td id="vkfgv4_36"><p>application/json</p></td></tr><tr id="vkfgv4_30"><td id="vkfgv4_37"><p>400</p></td><td id="vkfgv4_38"></td><td id="vkfgv4_39"></td></tr></tbody></table></div><p id="vkfgv4_26">Schema for response <code class="code" id="vkfgv4_40">201</code> (<code class="code" id="vkfgv4_41">application/json</code>):</p><div class="code-block" data-lang="json">
{}
</div></section><div class="last-modified">17 March 2026</div><div data-feedback-placeholder="true"></div><div class="navigation-links _bottom"><a href="listorderitems.html" class="navigation-links__prev">List order items</a><a href="updateorderitem.html" class="navigation-links__next">Update order item</a></div></article><div id="disqus_thread"></div></div></section></main></div><script src="https://resources.jetbrains.com/writerside/apidoc/6.26.0-b913/app.js"></script></body></html>
@@ -0,0 +1,38 @@
<!DOCTYPE html SYSTEM "about:legacy-compat">
<html lang="en-US" data-preset="contrast" data-primary-color="#307FFF"><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8"><meta charset="UTF-8"><meta name="robots" content="noindex"><meta name="built-on" content="2026-03-17T15:41:57.012514428"><title>Add plate scanner | Copenhagen Truck Wash API</title><script type="application/json" id="virtual-toc-data">[{"id":"endpoint","level":0,"title":"Endpoint","anchor":"#endpoint"},{"id":"operation","level":0,"title":"Operation","anchor":"#operation"},{"id":"authentication","level":0,"title":"Authentication","anchor":"#authentication"},{"id":"request-body","level":0,"title":"Request Body","anchor":"#request-body"},{"id":"responses","level":0,"title":"Responses","anchor":"#responses"}]</script><script type="application/json" id="topic-shortcuts"></script><link href="https://resources.jetbrains.com/writerside/apidoc/6.26.0-b913/app.css" rel="stylesheet"><meta name="msapplication-TileColor" content="#000000"><link rel="apple-touch-icon" sizes="180x180" href="https://jetbrains.com/apple-touch-icon.png"><link rel="icon" type="image/png" sizes="32x32" href="https://jetbrains.com/favicon-32x32.png"><link rel="icon" type="image/png" sizes="16x16" href="https://jetbrains.com/favicon-16x16.png"><meta name="msapplication-TileImage" content="https://resources.jetbrains.com/storage/ui/favicons/mstile-144x144.png"><meta name="msapplication-square70x70logo" content="https://resources.jetbrains.com/storage/ui/favicons/mstile-70x70.png"><meta name="msapplication-square150x150logo" content="https://resources.jetbrains.com/storage/ui/favicons/mstile-150x150.png"><meta name="msapplication-wide310x150logo" content="https://resources.jetbrains.com/storage/ui/favicons/mstile-310x150.png"><meta name="msapplication-square310x310logo" content="https://resources.jetbrains.com/storage/ui/favicons/mstile-310x310.png"><meta name="image" content=""><!-- Open Graph --><meta property="og:title" content="Add plate scanner | Copenhagen Truck Wash API"><meta property="og:description" content=""><meta property="og:image" content=""><meta property="og:site_name" content="Copenhagen Truck Wash API Help"><meta property="og:type" content="website"><meta property="og:locale" content="en_US"><meta property="og:url" content="writerside-documentation/addplatescanner.html"><!-- End Open Graph --><!-- Twitter Card --><meta name="twitter:card" content="summary_large_image"><meta name="twitter:site" content=""><meta name="twitter:title" content="Add plate scanner | Copenhagen Truck Wash API"><meta name="twitter:description" content=""><meta name="twitter:creator" content=""><meta name="twitter:image:src" content=""><!-- End Twitter Card --><!-- Schema.org WebPage --><script type="application/ld+json">{
"@context": "http://schema.org",
"@type": "WebPage",
"@id": "writerside-documentation/addplatescanner.html#webpage",
"url": "writerside-documentation/addplatescanner.html",
"name": "Add plate scanner | Copenhagen Truck Wash API",
"description": "",
"image": "",
"inLanguage":"en-US"
}</script><!-- End Schema.org --><!-- Schema.org WebSite --><script type="application/ld+json">{
"@type": "WebSite",
"@id": "writerside-documentation/#website",
"url": "writerside-documentation/",
"name": "Copenhagen Truck Wash API Help"
}</script><!-- End Schema.org --></head><body data-id="addPlateScanner" data-main-title="Add plate scanner" data-article-props="{&quot;seeAlsoStyle&quot;:&quot;links&quot;}" data-template="article" data-breadcrumbs="API-Reference.topic|API Reference///Tag_Plate_Scans.topic|Plate Scans///Tag_Plate_Scans_Page_1.topic|Plate Scans - Page 1 of 1"><div class="wrapper"><main class="panel _main"><header class="panel__header"><div class="container"><h3>Copenhagen Truck Wash API Help</h3><div class="panel-trigger"></div></div></header><section class="panel__content"><div class="container"><article class="article" data-shortcut-switcher="inactive"><h1 data-toc="addPlateScanner" id="addPlateScanner.topic">Add plate scanner</h1><p id="mcro17_2">This endpoint documentation is generated directly from <code class="code" id="mcro17_8">openapi.yaml</code>.</p><section class="chapter"><h2 id="endpoint" data-toc="endpoint">Endpoint</h2><div class="code-block" data-lang="http">POST /numberplatescanners</div></section><section class="chapter"><h2 id="operation" data-toc="operation">Operation</h2><p id="mcro17_10">Operation ID: <code class="code" id="mcro17_12">addPlateScanner</code></p><p id="mcro17_11">Add plate scanner</p></section><section class="chapter"><h2 id="authentication" data-toc="authentication">Authentication</h2><p id="mcro17_13">Security requirements:</p><div class="table-wrapper"><table class="wide" id="mcro17_14"><thead><tr class="ijRowHead" id="mcro17_15"><th id="mcro17_17"><p>Scheme</p></th><th id="mcro17_18"><p>Scopes</p></th></tr></thead><tbody><tr id="mcro17_16"><td id="mcro17_19"><p>BearerAuth</p></td><td id="mcro17_20"><p>-</p></td></tr></tbody></table></div></section><section class="chapter"><h2 id="request-body" data-toc="request-body">Request Body</h2><p id="mcro17_21">Required: yes.</p><p id="mcro17_22">Content type: <code class="code" id="mcro17_24">application/json</code></p><div class="code-block" data-lang="json">
{
&quot;properties&quot;: {
&quot;department_id&quot;: {
&quot;type&quot;: &quot;integer&quot;
},
&quot;name&quot;: {
&quot;type&quot;: &quot;string&quot;
},
&quot;notes&quot;: {
&quot;type&quot;: &quot;string&quot;
}
},
&quot;required&quot;: [
&quot;department_id&quot;,
&quot;name&quot;,
&quot;notes&quot;
],
&quot;type&quot;: &quot;object&quot;
}
</div></section><section class="chapter"><h2 id="responses" data-toc="responses">Responses</h2><div class="table-wrapper"><table class="wide" id="mcro17_25"><thead><tr class="ijRowHead" id="mcro17_28"><th id="mcro17_30"><p>Status</p></th><th id="mcro17_31"><p>Description</p></th><th id="mcro17_32"><p>Content Types</p></th></tr></thead><tbody><tr id="mcro17_29"><td id="mcro17_33"><p>201</p></td><td id="mcro17_34"><p>Plate scanner added successfully</p></td><td id="mcro17_35"><p>application/json</p></td></tr></tbody></table></div><p id="mcro17_26">Schema for response <code class="code" id="mcro17_36">201</code> (<code class="code" id="mcro17_37">application/json</code>):</p><div class="code-block" data-lang="json">
{}
</div></section><div class="last-modified">17 March 2026</div><div data-feedback-placeholder="true"></div><div class="navigation-links _bottom"><a href="listplatescanners.html" class="navigation-links__prev">List plate scanners</a><a href="updateplatescanner.html" class="navigation-links__next">Update plate scanner</a></div></article><div id="disqus_thread"></div></div></section></main></div><script src="https://resources.jetbrains.com/writerside/apidoc/6.26.0-b913/app.js"></script></body></html>
@@ -0,0 +1,30 @@
<!DOCTYPE html SYSTEM "about:legacy-compat">
<html lang="en-US" data-preset="contrast" data-primary-color="#307FFF"><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8"><meta charset="UTF-8"><meta name="robots" content="noindex"><meta name="built-on" content="2026-03-17T15:40:13.488605147"><title>Add role | Copenhagen Truck Wash API</title><script type="application/json" id="virtual-toc-data">[{"id":"endpoint","level":0,"title":"Endpoint","anchor":"#endpoint"},{"id":"operation","level":0,"title":"Operation","anchor":"#operation"},{"id":"authentication","level":0,"title":"Authentication","anchor":"#authentication"},{"id":"request-body","level":0,"title":"Request Body","anchor":"#request-body"},{"id":"responses","level":0,"title":"Responses","anchor":"#responses"}]</script><script type="application/json" id="topic-shortcuts"></script><link href="https://resources.jetbrains.com/writerside/apidoc/6.26.0-b913/app.css" rel="stylesheet"><meta name="msapplication-TileColor" content="#000000"><link rel="apple-touch-icon" sizes="180x180" href="https://jetbrains.com/apple-touch-icon.png"><link rel="icon" type="image/png" sizes="32x32" href="https://jetbrains.com/favicon-32x32.png"><link rel="icon" type="image/png" sizes="16x16" href="https://jetbrains.com/favicon-16x16.png"><meta name="msapplication-TileImage" content="https://resources.jetbrains.com/storage/ui/favicons/mstile-144x144.png"><meta name="msapplication-square70x70logo" content="https://resources.jetbrains.com/storage/ui/favicons/mstile-70x70.png"><meta name="msapplication-square150x150logo" content="https://resources.jetbrains.com/storage/ui/favicons/mstile-150x150.png"><meta name="msapplication-wide310x150logo" content="https://resources.jetbrains.com/storage/ui/favicons/mstile-310x150.png"><meta name="msapplication-square310x310logo" content="https://resources.jetbrains.com/storage/ui/favicons/mstile-310x310.png"><meta name="image" content=""><!-- Open Graph --><meta property="og:title" content="Add role | Copenhagen Truck Wash API"><meta property="og:description" content=""><meta property="og:image" content=""><meta property="og:site_name" content="Copenhagen Truck Wash API Help"><meta property="og:type" content="website"><meta property="og:locale" content="en_US"><meta property="og:url" content="writerside-documentation/addrole.html"><!-- End Open Graph --><!-- Twitter Card --><meta name="twitter:card" content="summary_large_image"><meta name="twitter:site" content=""><meta name="twitter:title" content="Add role | Copenhagen Truck Wash API"><meta name="twitter:description" content=""><meta name="twitter:creator" content=""><meta name="twitter:image:src" content=""><!-- End Twitter Card --><!-- Schema.org WebPage --><script type="application/ld+json">{
"@context": "http://schema.org",
"@type": "WebPage",
"@id": "writerside-documentation/addrole.html#webpage",
"url": "writerside-documentation/addrole.html",
"name": "Add role | Copenhagen Truck Wash API",
"description": "",
"image": "",
"inLanguage":"en-US"
}</script><!-- End Schema.org --><!-- Schema.org WebSite --><script type="application/ld+json">{
"@type": "WebSite",
"@id": "writerside-documentation/#website",
"url": "writerside-documentation/",
"name": "Copenhagen Truck Wash API Help"
}</script><!-- End Schema.org --></head><body data-id="addRole" data-main-title="Add role" data-article-props="{&quot;seeAlsoStyle&quot;:&quot;links&quot;}" data-template="article" data-breadcrumbs="API-Reference.topic|API Reference///Tag_Roles.topic|Roles///Tag_Roles_Page_1.topic|Roles - Page 1 of 1"><div class="wrapper"><main class="panel _main"><header class="panel__header"><div class="container"><h3>Copenhagen Truck Wash API Help</h3><div class="panel-trigger"></div></div></header><section class="panel__content"><div class="container"><article class="article" data-shortcut-switcher="inactive"><h1 data-toc="addRole" id="addRole.topic">Add role</h1><p id="-yg849z_2">This endpoint documentation is generated directly from <code class="code" id="-yg849z_8">openapi.yaml</code>.</p><section class="chapter"><h2 id="endpoint" data-toc="endpoint">Endpoint</h2><div class="code-block" data-lang="http">POST /roles</div></section><section class="chapter"><h2 id="operation" data-toc="operation">Operation</h2><p id="-yg849z_10">Operation ID: <code class="code" id="-yg849z_12">addRole</code></p><p id="-yg849z_11">Add role</p></section><section class="chapter"><h2 id="authentication" data-toc="authentication">Authentication</h2><p id="-yg849z_13">Security requirements:</p><div class="table-wrapper"><table class="wide" id="-yg849z_14"><thead><tr class="ijRowHead" id="-yg849z_15"><th id="-yg849z_17"><p>Scheme</p></th><th id="-yg849z_18"><p>Scopes</p></th></tr></thead><tbody><tr id="-yg849z_16"><td id="-yg849z_19"><p>BearerAuth</p></td><td id="-yg849z_20"><p>-</p></td></tr></tbody></table></div></section><section class="chapter"><h2 id="request-body" data-toc="request-body">Request Body</h2><p id="-yg849z_21">Required: yes.</p><p id="-yg849z_22">Content type: <code class="code" id="-yg849z_24">application/json</code></p><div class="code-block" data-lang="json">
{
&quot;properties&quot;: {
&quot;name&quot;: {
&quot;type&quot;: &quot;string&quot;
}
},
&quot;required&quot;: [
&quot;name&quot;
],
&quot;type&quot;: &quot;object&quot;
}
</div></section><section class="chapter"><h2 id="responses" data-toc="responses">Responses</h2><div class="table-wrapper"><table class="wide" id="-yg849z_25"><thead><tr class="ijRowHead" id="-yg849z_28"><th id="-yg849z_30"><p>Status</p></th><th id="-yg849z_31"><p>Description</p></th><th id="-yg849z_32"><p>Content Types</p></th></tr></thead><tbody><tr id="-yg849z_29"><td id="-yg849z_33"><p>200</p></td><td id="-yg849z_34"><p>Success</p></td><td id="-yg849z_35"><p>application/json</p></td></tr></tbody></table></div><p id="-yg849z_26">Schema for response <code class="code" id="-yg849z_36">200</code> (<code class="code" id="-yg849z_37">application/json</code>):</p><div class="code-block" data-lang="json">
{}
</div></section><div class="last-modified">17 March 2026</div><div data-feedback-placeholder="true"></div><div class="navigation-links _bottom"><a href="listroles.html" class="navigation-links__prev">List roles</a><a href="editrole.html" class="navigation-links__next">Edit role</a></div></article><div id="disqus_thread"></div></div></section></main></div><script src="https://resources.jetbrains.com/writerside/apidoc/6.26.0-b913/app.js"></script></body></html>
@@ -0,0 +1,34 @@
<!DOCTYPE html SYSTEM "about:legacy-compat">
<html lang="en-US" data-preset="contrast" data-primary-color="#307FFF"><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8"><meta charset="UTF-8"><meta name="robots" content="noindex"><meta name="built-on" content="2026-03-17T15:41:50.885972622"><title>Add permission to role | Copenhagen Truck Wash API</title><script type="application/json" id="virtual-toc-data">[{"id":"endpoint","level":0,"title":"Endpoint","anchor":"#endpoint"},{"id":"operation","level":0,"title":"Operation","anchor":"#operation"},{"id":"authentication","level":0,"title":"Authentication","anchor":"#authentication"},{"id":"request-body","level":0,"title":"Request Body","anchor":"#request-body"},{"id":"responses","level":0,"title":"Responses","anchor":"#responses"}]</script><script type="application/json" id="topic-shortcuts"></script><link href="https://resources.jetbrains.com/writerside/apidoc/6.26.0-b913/app.css" rel="stylesheet"><meta name="msapplication-TileColor" content="#000000"><link rel="apple-touch-icon" sizes="180x180" href="https://jetbrains.com/apple-touch-icon.png"><link rel="icon" type="image/png" sizes="32x32" href="https://jetbrains.com/favicon-32x32.png"><link rel="icon" type="image/png" sizes="16x16" href="https://jetbrains.com/favicon-16x16.png"><meta name="msapplication-TileImage" content="https://resources.jetbrains.com/storage/ui/favicons/mstile-144x144.png"><meta name="msapplication-square70x70logo" content="https://resources.jetbrains.com/storage/ui/favicons/mstile-70x70.png"><meta name="msapplication-square150x150logo" content="https://resources.jetbrains.com/storage/ui/favicons/mstile-150x150.png"><meta name="msapplication-wide310x150logo" content="https://resources.jetbrains.com/storage/ui/favicons/mstile-310x150.png"><meta name="msapplication-square310x310logo" content="https://resources.jetbrains.com/storage/ui/favicons/mstile-310x310.png"><meta name="image" content=""><!-- Open Graph --><meta property="og:title" content="Add permission to role | Copenhagen Truck Wash API"><meta property="og:description" content=""><meta property="og:image" content=""><meta property="og:site_name" content="Copenhagen Truck Wash API Help"><meta property="og:type" content="website"><meta property="og:locale" content="en_US"><meta property="og:url" content="writerside-documentation/addrolepermission.html"><!-- End Open Graph --><!-- Twitter Card --><meta name="twitter:card" content="summary_large_image"><meta name="twitter:site" content=""><meta name="twitter:title" content="Add permission to role | Copenhagen Truck Wash API"><meta name="twitter:description" content=""><meta name="twitter:creator" content=""><meta name="twitter:image:src" content=""><!-- End Twitter Card --><!-- Schema.org WebPage --><script type="application/ld+json">{
"@context": "http://schema.org",
"@type": "WebPage",
"@id": "writerside-documentation/addrolepermission.html#webpage",
"url": "writerside-documentation/addrolepermission.html",
"name": "Add permission to role | Copenhagen Truck Wash API",
"description": "",
"image": "",
"inLanguage":"en-US"
}</script><!-- End Schema.org --><!-- Schema.org WebSite --><script type="application/ld+json">{
"@type": "WebSite",
"@id": "writerside-documentation/#website",
"url": "writerside-documentation/",
"name": "Copenhagen Truck Wash API Help"
}</script><!-- End Schema.org --></head><body data-id="addRolePermission" data-main-title="Add permission to role" data-article-props="{&quot;seeAlsoStyle&quot;:&quot;links&quot;}" data-template="article" data-breadcrumbs="API-Reference.topic|API Reference///Tag_Roles.topic|Roles///Tag_Roles_Page_1.topic|Roles - Page 1 of 1"><div class="wrapper"><main class="panel _main"><header class="panel__header"><div class="container"><h3>Copenhagen Truck Wash API Help</h3><div class="panel-trigger"></div></div></header><section class="panel__content"><div class="container"><article class="article" data-shortcut-switcher="inactive"><h1 data-toc="addRolePermission" id="addRolePermission.topic">Add permission to role</h1><p id="fnxwco_2">This endpoint documentation is generated directly from <code class="code" id="fnxwco_8">openapi.yaml</code>.</p><section class="chapter"><h2 id="endpoint" data-toc="endpoint">Endpoint</h2><div class="code-block" data-lang="http">POST /roles/permissions</div></section><section class="chapter"><h2 id="operation" data-toc="operation">Operation</h2><p id="fnxwco_10">Operation ID: <code class="code" id="fnxwco_12">addRolePermission</code></p><p id="fnxwco_11">Add permission to role</p></section><section class="chapter"><h2 id="authentication" data-toc="authentication">Authentication</h2><p id="fnxwco_13">Security requirements:</p><div class="table-wrapper"><table class="wide" id="fnxwco_14"><thead><tr class="ijRowHead" id="fnxwco_15"><th id="fnxwco_17"><p>Scheme</p></th><th id="fnxwco_18"><p>Scopes</p></th></tr></thead><tbody><tr id="fnxwco_16"><td id="fnxwco_19"><p>BearerAuth</p></td><td id="fnxwco_20"><p>-</p></td></tr></tbody></table></div></section><section class="chapter"><h2 id="request-body" data-toc="request-body">Request Body</h2><p id="fnxwco_21">Required: yes.</p><p id="fnxwco_22">Content type: <code class="code" id="fnxwco_24">application/json</code></p><div class="code-block" data-lang="json">
{
&quot;properties&quot;: {
&quot;permission&quot;: {
&quot;type&quot;: &quot;string&quot;
},
&quot;role_id&quot;: {
&quot;type&quot;: &quot;integer&quot;
}
},
&quot;required&quot;: [
&quot;role_id&quot;,
&quot;permission&quot;
],
&quot;type&quot;: &quot;object&quot;
}
</div></section><section class="chapter"><h2 id="responses" data-toc="responses">Responses</h2><div class="table-wrapper"><table class="wide" id="fnxwco_25"><thead><tr class="ijRowHead" id="fnxwco_28"><th id="fnxwco_30"><p>Status</p></th><th id="fnxwco_31"><p>Description</p></th><th id="fnxwco_32"><p>Content Types</p></th></tr></thead><tbody><tr id="fnxwco_29"><td id="fnxwco_33"><p>200</p></td><td id="fnxwco_34"><p>Success</p></td><td id="fnxwco_35"><p>application/json</p></td></tr></tbody></table></div><p id="fnxwco_26">Schema for response <code class="code" id="fnxwco_36">200</code> (<code class="code" id="fnxwco_37">application/json</code>):</p><div class="code-block" data-lang="json">
{}
</div></section><div class="last-modified">17 March 2026</div><div data-feedback-placeholder="true"></div><div class="navigation-links _bottom"><a href="removerolepermission.html" class="navigation-links__prev">Remove permission from role</a><a href="tag-self-serve.html" class="navigation-links__next">Self-Serve</a></div></article><div id="disqus_thread"></div></div></section></main></div><script src="https://resources.jetbrains.com/writerside/apidoc/6.26.0-b913/app.js"></script></body></html>
@@ -0,0 +1,56 @@
<!DOCTYPE html SYSTEM "about:legacy-compat">
<html lang="en-US" data-preset="contrast" data-primary-color="#307FFF"><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8"><meta charset="UTF-8"><meta name="robots" content="noindex"><meta name="built-on" content="2026-03-17T15:44:43.615253374"><title>Add self-serve condition | Copenhagen Truck Wash API</title><script type="application/json" id="virtual-toc-data">[{"id":"endpoint","level":0,"title":"Endpoint","anchor":"#endpoint"},{"id":"operation","level":0,"title":"Operation","anchor":"#operation"},{"id":"authentication","level":0,"title":"Authentication","anchor":"#authentication"},{"id":"request-body","level":0,"title":"Request Body","anchor":"#request-body"},{"id":"responses","level":0,"title":"Responses","anchor":"#responses"}]</script><script type="application/json" id="topic-shortcuts"></script><link href="https://resources.jetbrains.com/writerside/apidoc/6.26.0-b913/app.css" rel="stylesheet"><meta name="msapplication-TileColor" content="#000000"><link rel="apple-touch-icon" sizes="180x180" href="https://jetbrains.com/apple-touch-icon.png"><link rel="icon" type="image/png" sizes="32x32" href="https://jetbrains.com/favicon-32x32.png"><link rel="icon" type="image/png" sizes="16x16" href="https://jetbrains.com/favicon-16x16.png"><meta name="msapplication-TileImage" content="https://resources.jetbrains.com/storage/ui/favicons/mstile-144x144.png"><meta name="msapplication-square70x70logo" content="https://resources.jetbrains.com/storage/ui/favicons/mstile-70x70.png"><meta name="msapplication-square150x150logo" content="https://resources.jetbrains.com/storage/ui/favicons/mstile-150x150.png"><meta name="msapplication-wide310x150logo" content="https://resources.jetbrains.com/storage/ui/favicons/mstile-310x150.png"><meta name="msapplication-square310x310logo" content="https://resources.jetbrains.com/storage/ui/favicons/mstile-310x310.png"><meta name="image" content=""><!-- Open Graph --><meta property="og:title" content="Add self-serve condition | Copenhagen Truck Wash API"><meta property="og:description" content=""><meta property="og:image" content=""><meta property="og:site_name" content="Copenhagen Truck Wash API Help"><meta property="og:type" content="website"><meta property="og:locale" content="en_US"><meta property="og:url" content="writerside-documentation/addselfservecondition.html"><!-- End Open Graph --><!-- Twitter Card --><meta name="twitter:card" content="summary_large_image"><meta name="twitter:site" content=""><meta name="twitter:title" content="Add self-serve condition | Copenhagen Truck Wash API"><meta name="twitter:description" content=""><meta name="twitter:creator" content=""><meta name="twitter:image:src" content=""><!-- End Twitter Card --><!-- Schema.org WebPage --><script type="application/ld+json">{
"@context": "http://schema.org",
"@type": "WebPage",
"@id": "writerside-documentation/addselfservecondition.html#webpage",
"url": "writerside-documentation/addselfservecondition.html",
"name": "Add self-serve condition | Copenhagen Truck Wash API",
"description": "",
"image": "",
"inLanguage":"en-US"
}</script><!-- End Schema.org --><!-- Schema.org WebSite --><script type="application/ld+json">{
"@type": "WebSite",
"@id": "writerside-documentation/#website",
"url": "writerside-documentation/",
"name": "Copenhagen Truck Wash API Help"
}</script><!-- End Schema.org --></head><body data-id="addSelfserveCondition" data-main-title="Add self-serve condition" data-article-props="{&quot;seeAlsoStyle&quot;:&quot;links&quot;}" data-template="article" data-breadcrumbs="API-Reference.topic|API Reference///Tag_Self_Serve.topic|Self-Serve///Tag_Self_Serve_Page_1.topic|Self-Serve - Page 1 of 2"><div class="wrapper"><main class="panel _main"><header class="panel__header"><div class="container"><h3>Copenhagen Truck Wash API Help</h3><div class="panel-trigger"></div></div></header><section class="panel__content"><div class="container"><article class="article" data-shortcut-switcher="inactive"><h1 data-toc="addSelfserveCondition" id="addSelfserveCondition.topic">Add self-serve condition</h1><p id="-o0z7d1_2">This endpoint documentation is generated directly from <code class="code" id="-o0z7d1_8">openapi.yaml</code>.</p><section class="chapter"><h2 id="endpoint" data-toc="endpoint">Endpoint</h2><div class="code-block" data-lang="http">POST /department/selfserve/conditions</div></section><section class="chapter"><h2 id="operation" data-toc="operation">Operation</h2><p id="-o0z7d1_10">Operation ID: <code class="code" id="-o0z7d1_12">addSelfserveCondition</code></p><p id="-o0z7d1_11">Add a new self-serve condition. Either provide a reusable machine_type_id or a legacy department/lane/product scope.</p></section><section class="chapter"><h2 id="authentication" data-toc="authentication">Authentication</h2><p id="-o0z7d1_13">Security requirements:</p><div class="table-wrapper"><table class="wide" id="-o0z7d1_14"><thead><tr class="ijRowHead" id="-o0z7d1_15"><th id="-o0z7d1_17"><p>Scheme</p></th><th id="-o0z7d1_18"><p>Scopes</p></th></tr></thead><tbody><tr id="-o0z7d1_16"><td id="-o0z7d1_19"><p>BearerAuth</p></td><td id="-o0z7d1_20"><p>-</p></td></tr></tbody></table></div></section><section class="chapter"><h2 id="request-body" data-toc="request-body">Request Body</h2><p id="-o0z7d1_21">Required: yes.</p><p id="-o0z7d1_22">Content type: <code class="code" id="-o0z7d1_24">application/json</code></p><div class="code-block" data-lang="json">
{
&quot;properties&quot;: {
&quot;condition_id&quot;: {
&quot;nullable&quot;: true,
&quot;type&quot;: &quot;integer&quot;
},
&quot;department&quot;: {
&quot;default&quot;: 0,
&quot;type&quot;: &quot;integer&quot;
},
&quot;description&quot;: {
&quot;type&quot;: &quot;string&quot;
},
&quot;lane&quot;: {
&quot;default&quot;: 0,
&quot;type&quot;: &quot;integer&quot;
},
&quot;machine_type_id&quot;: {
&quot;nullable&quot;: true,
&quot;type&quot;: &quot;integer&quot;
},
&quot;name&quot;: {
&quot;type&quot;: &quot;string&quot;
},
&quot;product&quot;: {
&quot;default&quot;: 0,
&quot;type&quot;: &quot;integer&quot;
}
},
&quot;required&quot;: [
&quot;name&quot;,
&quot;description&quot;
],
&quot;type&quot;: &quot;object&quot;
}
</div></section><section class="chapter"><h2 id="responses" data-toc="responses">Responses</h2><div class="table-wrapper"><table class="wide" id="-o0z7d1_25"><thead><tr class="ijRowHead" id="-o0z7d1_28"><th id="-o0z7d1_32"><p>Status</p></th><th id="-o0z7d1_33"><p>Description</p></th><th id="-o0z7d1_34"><p>Content Types</p></th></tr></thead><tbody><tr id="-o0z7d1_29"><td id="-o0z7d1_35"><p>200</p></td><td id="-o0z7d1_36"><p>Successfully added condition</p></td><td id="-o0z7d1_37"><p>application/json</p></td></tr><tr id="-o0z7d1_30"><td id="-o0z7d1_38"><p>400</p></td><td id="-o0z7d1_39"></td><td id="-o0z7d1_40"></td></tr><tr id="-o0z7d1_31"><td id="-o0z7d1_41"><p>500</p></td><td id="-o0z7d1_42"></td><td id="-o0z7d1_43"></td></tr></tbody></table></div><p id="-o0z7d1_26">Schema for response <code class="code" id="-o0z7d1_44">200</code> (<code class="code" id="-o0z7d1_45">application/json</code>):</p><div class="code-block" data-lang="json">
{
&quot;$ref&quot;: &quot;#/components/schemas/DepartmentSelfserveCondition&quot;
}
</div></section><div class="last-modified">17 March 2026</div><div data-feedback-placeholder="true"></div><div class="navigation-links _bottom"><a href="listselfserveconditions.html" class="navigation-links__prev">List self-serve conditions</a><a href="updateselfservecondition.html" class="navigation-links__next">Update self-serve condition</a></div></article><div id="disqus_thread"></div></div></section></main></div><script src="https://resources.jetbrains.com/writerside/apidoc/6.26.0-b913/app.js"></script></body></html>
@@ -0,0 +1,52 @@
<!DOCTYPE html SYSTEM "about:legacy-compat">
<html lang="en-US" data-preset="contrast" data-primary-color="#307FFF"><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8"><meta charset="UTF-8"><meta name="robots" content="noindex"><meta name="built-on" content="2026-03-17T15:43:15.046696864"><title>Add self-serve condition rule | Copenhagen Truck Wash API</title><script type="application/json" id="virtual-toc-data">[{"id":"endpoint","level":0,"title":"Endpoint","anchor":"#endpoint"},{"id":"operation","level":0,"title":"Operation","anchor":"#operation"},{"id":"authentication","level":0,"title":"Authentication","anchor":"#authentication"},{"id":"request-body","level":0,"title":"Request Body","anchor":"#request-body"},{"id":"responses","level":0,"title":"Responses","anchor":"#responses"}]</script><script type="application/json" id="topic-shortcuts"></script><link href="https://resources.jetbrains.com/writerside/apidoc/6.26.0-b913/app.css" rel="stylesheet"><meta name="msapplication-TileColor" content="#000000"><link rel="apple-touch-icon" sizes="180x180" href="https://jetbrains.com/apple-touch-icon.png"><link rel="icon" type="image/png" sizes="32x32" href="https://jetbrains.com/favicon-32x32.png"><link rel="icon" type="image/png" sizes="16x16" href="https://jetbrains.com/favicon-16x16.png"><meta name="msapplication-TileImage" content="https://resources.jetbrains.com/storage/ui/favicons/mstile-144x144.png"><meta name="msapplication-square70x70logo" content="https://resources.jetbrains.com/storage/ui/favicons/mstile-70x70.png"><meta name="msapplication-square150x150logo" content="https://resources.jetbrains.com/storage/ui/favicons/mstile-150x150.png"><meta name="msapplication-wide310x150logo" content="https://resources.jetbrains.com/storage/ui/favicons/mstile-310x150.png"><meta name="msapplication-square310x310logo" content="https://resources.jetbrains.com/storage/ui/favicons/mstile-310x310.png"><meta name="image" content=""><!-- Open Graph --><meta property="og:title" content="Add self-serve condition rule | Copenhagen Truck Wash API"><meta property="og:description" content=""><meta property="og:image" content=""><meta property="og:site_name" content="Copenhagen Truck Wash API Help"><meta property="og:type" content="website"><meta property="og:locale" content="en_US"><meta property="og:url" content="writerside-documentation/addselfserveconditionrule.html"><!-- End Open Graph --><!-- Twitter Card --><meta name="twitter:card" content="summary_large_image"><meta name="twitter:site" content=""><meta name="twitter:title" content="Add self-serve condition rule | Copenhagen Truck Wash API"><meta name="twitter:description" content=""><meta name="twitter:creator" content=""><meta name="twitter:image:src" content=""><!-- End Twitter Card --><!-- Schema.org WebPage --><script type="application/ld+json">{
"@context": "http://schema.org",
"@type": "WebPage",
"@id": "writerside-documentation/addselfserveconditionrule.html#webpage",
"url": "writerside-documentation/addselfserveconditionrule.html",
"name": "Add self-serve condition rule | Copenhagen Truck Wash API",
"description": "",
"image": "",
"inLanguage":"en-US"
}</script><!-- End Schema.org --><!-- Schema.org WebSite --><script type="application/ld+json">{
"@type": "WebSite",
"@id": "writerside-documentation/#website",
"url": "writerside-documentation/",
"name": "Copenhagen Truck Wash API Help"
}</script><!-- End Schema.org --></head><body data-id="addSelfserveConditionRule" data-main-title="Add self-serve condition rule" data-article-props="{&quot;seeAlsoStyle&quot;:&quot;links&quot;}" data-template="article" data-breadcrumbs="API-Reference.topic|API Reference///Tag_Self_Serve.topic|Self-Serve///Tag_Self_Serve_Page_1.topic|Self-Serve - Page 1 of 2"><div class="wrapper"><main class="panel _main"><header class="panel__header"><div class="container"><h3>Copenhagen Truck Wash API Help</h3><div class="panel-trigger"></div></div></header><section class="panel__content"><div class="container"><article class="article" data-shortcut-switcher="inactive"><h1 data-toc="addSelfserveConditionRule" id="addSelfserveConditionRule.topic">Add self-serve condition rule</h1><p id="-sr43qh_2">This endpoint documentation is generated directly from <code class="code" id="-sr43qh_8">openapi.yaml</code>.</p><section class="chapter"><h2 id="endpoint" data-toc="endpoint">Endpoint</h2><div class="code-block" data-lang="http">POST /department/selfserve/condition/rules</div></section><section class="chapter"><h2 id="operation" data-toc="operation">Operation</h2><p id="-sr43qh_10">Operation ID: <code class="code" id="-sr43qh_12">addSelfserveConditionRule</code></p><p id="-sr43qh_11">Add a new self-serve condition rule.</p></section><section class="chapter"><h2 id="authentication" data-toc="authentication">Authentication</h2><p id="-sr43qh_13">Security requirements:</p><div class="table-wrapper"><table class="wide" id="-sr43qh_14"><thead><tr class="ijRowHead" id="-sr43qh_15"><th id="-sr43qh_17"><p>Scheme</p></th><th id="-sr43qh_18"><p>Scopes</p></th></tr></thead><tbody><tr id="-sr43qh_16"><td id="-sr43qh_19"><p>BearerAuth</p></td><td id="-sr43qh_20"><p>-</p></td></tr></tbody></table></div></section><section class="chapter"><h2 id="request-body" data-toc="request-body">Request Body</h2><p id="-sr43qh_21">Required: yes.</p><p id="-sr43qh_22">Content type: <code class="code" id="-sr43qh_24">application/json</code></p><div class="code-block" data-lang="json">
{
&quot;properties&quot;: {
&quot;condition_id&quot;: {
&quot;type&quot;: &quot;integer&quot;
},
&quot;description&quot;: {
&quot;type&quot;: &quot;string&quot;
},
&quot;name&quot;: {
&quot;type&quot;: &quot;string&quot;
},
&quot;object_id&quot;: {
&quot;type&quot;: &quot;integer&quot;
},
&quot;object_type&quot;: {
&quot;type&quot;: &quot;string&quot;
},
&quot;type&quot;: {
&quot;type&quot;: &quot;string&quot;
}
},
&quot;required&quot;: [
&quot;condition_id&quot;,
&quot;type&quot;,
&quot;object_type&quot;,
&quot;object_id&quot;,
&quot;name&quot;,
&quot;description&quot;
],
&quot;type&quot;: &quot;object&quot;
}
</div></section><section class="chapter"><h2 id="responses" data-toc="responses">Responses</h2><div class="table-wrapper"><table class="wide" id="-sr43qh_25"><thead><tr class="ijRowHead" id="-sr43qh_28"><th id="-sr43qh_32"><p>Status</p></th><th id="-sr43qh_33"><p>Description</p></th><th id="-sr43qh_34"><p>Content Types</p></th></tr></thead><tbody><tr id="-sr43qh_29"><td id="-sr43qh_35"><p>200</p></td><td id="-sr43qh_36"><p>Successfully added condition rule</p></td><td id="-sr43qh_37"><p>application/json</p></td></tr><tr id="-sr43qh_30"><td id="-sr43qh_38"><p>400</p></td><td id="-sr43qh_39"></td><td id="-sr43qh_40"></td></tr><tr id="-sr43qh_31"><td id="-sr43qh_41"><p>500</p></td><td id="-sr43qh_42"></td><td id="-sr43qh_43"></td></tr></tbody></table></div><p id="-sr43qh_26">Schema for response <code class="code" id="-sr43qh_44">200</code> (<code class="code" id="-sr43qh_45">application/json</code>):</p><div class="code-block" data-lang="json">
{
&quot;$ref&quot;: &quot;#/components/schemas/DepartmentSelfserveConditionRule&quot;
}
</div></section><div class="last-modified">17 March 2026</div><div data-feedback-placeholder="true"></div><div class="navigation-links _bottom"><a href="listselfserveconditionrules.html" class="navigation-links__prev">List self-serve condition rules</a><a href="updateselfserveconditionrule.html" class="navigation-links__next">Update self-serve condition rule</a></div></article><div id="disqus_thread"></div></div></section></main></div><script src="https://resources.jetbrains.com/writerside/apidoc/6.26.0-b913/app.js"></script></body></html>
@@ -0,0 +1,36 @@
<!DOCTYPE html SYSTEM "about:legacy-compat">
<html lang="en-US" data-preset="contrast" data-primary-color="#307FFF"><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8"><meta charset="UTF-8"><meta name="robots" content="noindex"><meta name="built-on" content="2026-03-17T15:45:51.681265559"><title>Add reusable self-serve machine type | Copenhagen Truck Wash API</title><script type="application/json" id="virtual-toc-data">[{"id":"endpoint","level":0,"title":"Endpoint","anchor":"#endpoint"},{"id":"operation","level":0,"title":"Operation","anchor":"#operation"},{"id":"authentication","level":0,"title":"Authentication","anchor":"#authentication"},{"id":"request-body","level":0,"title":"Request Body","anchor":"#request-body"},{"id":"responses","level":0,"title":"Responses","anchor":"#responses"}]</script><script type="application/json" id="topic-shortcuts"></script><link href="https://resources.jetbrains.com/writerside/apidoc/6.26.0-b913/app.css" rel="stylesheet"><meta name="msapplication-TileColor" content="#000000"><link rel="apple-touch-icon" sizes="180x180" href="https://jetbrains.com/apple-touch-icon.png"><link rel="icon" type="image/png" sizes="32x32" href="https://jetbrains.com/favicon-32x32.png"><link rel="icon" type="image/png" sizes="16x16" href="https://jetbrains.com/favicon-16x16.png"><meta name="msapplication-TileImage" content="https://resources.jetbrains.com/storage/ui/favicons/mstile-144x144.png"><meta name="msapplication-square70x70logo" content="https://resources.jetbrains.com/storage/ui/favicons/mstile-70x70.png"><meta name="msapplication-square150x150logo" content="https://resources.jetbrains.com/storage/ui/favicons/mstile-150x150.png"><meta name="msapplication-wide310x150logo" content="https://resources.jetbrains.com/storage/ui/favicons/mstile-310x150.png"><meta name="msapplication-square310x310logo" content="https://resources.jetbrains.com/storage/ui/favicons/mstile-310x310.png"><meta name="image" content=""><!-- Open Graph --><meta property="og:title" content="Add reusable self-serve machine type | Copenhagen Truck Wash API"><meta property="og:description" content=""><meta property="og:image" content=""><meta property="og:site_name" content="Copenhagen Truck Wash API Help"><meta property="og:type" content="website"><meta property="og:locale" content="en_US"><meta property="og:url" content="writerside-documentation/addselfservemachinetype.html"><!-- End Open Graph --><!-- Twitter Card --><meta name="twitter:card" content="summary_large_image"><meta name="twitter:site" content=""><meta name="twitter:title" content="Add reusable self-serve machine type | Copenhagen Truck Wash API"><meta name="twitter:description" content=""><meta name="twitter:creator" content=""><meta name="twitter:image:src" content=""><!-- End Twitter Card --><!-- Schema.org WebPage --><script type="application/ld+json">{
"@context": "http://schema.org",
"@type": "WebPage",
"@id": "writerside-documentation/addselfservemachinetype.html#webpage",
"url": "writerside-documentation/addselfservemachinetype.html",
"name": "Add reusable self-serve machine type | Copenhagen Truck Wash API",
"description": "",
"image": "",
"inLanguage":"en-US"
}</script><!-- End Schema.org --><!-- Schema.org WebSite --><script type="application/ld+json">{
"@type": "WebSite",
"@id": "writerside-documentation/#website",
"url": "writerside-documentation/",
"name": "Copenhagen Truck Wash API Help"
}</script><!-- End Schema.org --></head><body data-id="addSelfserveMachineType" data-main-title="Add reusable self-serve machine type" data-article-props="{&quot;seeAlsoStyle&quot;:&quot;links&quot;}" data-template="article" data-breadcrumbs="API-Reference.topic|API Reference///Tag_Self_Serve.topic|Self-Serve///Tag_Self_Serve_Page_1.topic|Self-Serve - Page 1 of 2"><div class="wrapper"><main class="panel _main"><header class="panel__header"><div class="container"><h3>Copenhagen Truck Wash API Help</h3><div class="panel-trigger"></div></div></header><section class="panel__content"><div class="container"><article class="article" data-shortcut-switcher="inactive"><h1 data-toc="addSelfserveMachineType" id="addSelfserveMachineType.topic">Add reusable self-serve machine type</h1><p id="-gzddmn_2">This endpoint documentation is generated directly from <code class="code" id="-gzddmn_8">openapi.yaml</code>.</p><section class="chapter"><h2 id="endpoint" data-toc="endpoint">Endpoint</h2><div class="code-block" data-lang="http">POST /department/selfserve/machine-types</div></section><section class="chapter"><h2 id="operation" data-toc="operation">Operation</h2><p id="-gzddmn_10">Operation ID: <code class="code" id="-gzddmn_12">addSelfserveMachineType</code></p><p id="-gzddmn_11">Add reusable self-serve machine type</p></section><section class="chapter"><h2 id="authentication" data-toc="authentication">Authentication</h2><p id="-gzddmn_13">Security requirements:</p><div class="table-wrapper"><table class="wide" id="-gzddmn_14"><thead><tr class="ijRowHead" id="-gzddmn_15"><th id="-gzddmn_17"><p>Scheme</p></th><th id="-gzddmn_18"><p>Scopes</p></th></tr></thead><tbody><tr id="-gzddmn_16"><td id="-gzddmn_19"><p>BearerAuth</p></td><td id="-gzddmn_20"><p>-</p></td></tr></tbody></table></div></section><section class="chapter"><h2 id="request-body" data-toc="request-body">Request Body</h2><p id="-gzddmn_21">Required: yes.</p><p id="-gzddmn_22">Content type: <code class="code" id="-gzddmn_24">application/json</code></p><div class="code-block" data-lang="json">
{
&quot;properties&quot;: {
&quot;description&quot;: {
&quot;nullable&quot;: true,
&quot;type&quot;: &quot;string&quot;
},
&quot;name&quot;: {
&quot;type&quot;: &quot;string&quot;
}
},
&quot;required&quot;: [
&quot;name&quot;
],
&quot;type&quot;: &quot;object&quot;
}
</div></section><section class="chapter"><h2 id="responses" data-toc="responses">Responses</h2><div class="table-wrapper"><table class="wide" id="-gzddmn_25"><thead><tr class="ijRowHead" id="-gzddmn_28"><th id="-gzddmn_30"><p>Status</p></th><th id="-gzddmn_31"><p>Description</p></th><th id="-gzddmn_32"><p>Content Types</p></th></tr></thead><tbody><tr id="-gzddmn_29"><td id="-gzddmn_33"><p>200</p></td><td id="-gzddmn_34"><p>Successfully added machine type</p></td><td id="-gzddmn_35"><p>application/json</p></td></tr></tbody></table></div><p id="-gzddmn_26">Schema for response <code class="code" id="-gzddmn_36">200</code> (<code class="code" id="-gzddmn_37">application/json</code>):</p><div class="code-block" data-lang="json">
{
&quot;$ref&quot;: &quot;#/components/schemas/SelfserveMachineType&quot;
}
</div></section><div class="last-modified">17 March 2026</div><div data-feedback-placeholder="true"></div><div class="navigation-links _bottom"><a href="listselfservemachinetypes.html" class="navigation-links__prev">List reusable self-serve machine types</a><a href="updateselfservemachinetype.html" class="navigation-links__next">Update reusable self-serve machine type</a></div></article><div id="disqus_thread"></div></div></section></main></div><script src="https://resources.jetbrains.com/writerside/apidoc/6.26.0-b913/app.js"></script></body></html>
@@ -0,0 +1,56 @@
<!DOCTYPE html SYSTEM "about:legacy-compat">
<html lang="en-US" data-preset="contrast" data-primary-color="#307FFF"><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8"><meta charset="UTF-8"><meta name="robots" content="noindex"><meta name="built-on" content="2026-03-17T15:46:15.488004224"><title>Add self-serve question | Copenhagen Truck Wash API</title><script type="application/json" id="virtual-toc-data">[{"id":"endpoint","level":0,"title":"Endpoint","anchor":"#endpoint"},{"id":"operation","level":0,"title":"Operation","anchor":"#operation"},{"id":"authentication","level":0,"title":"Authentication","anchor":"#authentication"},{"id":"request-body","level":0,"title":"Request Body","anchor":"#request-body"},{"id":"responses","level":0,"title":"Responses","anchor":"#responses"}]</script><script type="application/json" id="topic-shortcuts"></script><link href="https://resources.jetbrains.com/writerside/apidoc/6.26.0-b913/app.css" rel="stylesheet"><meta name="msapplication-TileColor" content="#000000"><link rel="apple-touch-icon" sizes="180x180" href="https://jetbrains.com/apple-touch-icon.png"><link rel="icon" type="image/png" sizes="32x32" href="https://jetbrains.com/favicon-32x32.png"><link rel="icon" type="image/png" sizes="16x16" href="https://jetbrains.com/favicon-16x16.png"><meta name="msapplication-TileImage" content="https://resources.jetbrains.com/storage/ui/favicons/mstile-144x144.png"><meta name="msapplication-square70x70logo" content="https://resources.jetbrains.com/storage/ui/favicons/mstile-70x70.png"><meta name="msapplication-square150x150logo" content="https://resources.jetbrains.com/storage/ui/favicons/mstile-150x150.png"><meta name="msapplication-wide310x150logo" content="https://resources.jetbrains.com/storage/ui/favicons/mstile-310x150.png"><meta name="msapplication-square310x310logo" content="https://resources.jetbrains.com/storage/ui/favicons/mstile-310x310.png"><meta name="image" content=""><!-- Open Graph --><meta property="og:title" content="Add self-serve question | Copenhagen Truck Wash API"><meta property="og:description" content=""><meta property="og:image" content=""><meta property="og:site_name" content="Copenhagen Truck Wash API Help"><meta property="og:type" content="website"><meta property="og:locale" content="en_US"><meta property="og:url" content="writerside-documentation/addselfservequestion.html"><!-- End Open Graph --><!-- Twitter Card --><meta name="twitter:card" content="summary_large_image"><meta name="twitter:site" content=""><meta name="twitter:title" content="Add self-serve question | Copenhagen Truck Wash API"><meta name="twitter:description" content=""><meta name="twitter:creator" content=""><meta name="twitter:image:src" content=""><!-- End Twitter Card --><!-- Schema.org WebPage --><script type="application/ld+json">{
"@context": "http://schema.org",
"@type": "WebPage",
"@id": "writerside-documentation/addselfservequestion.html#webpage",
"url": "writerside-documentation/addselfservequestion.html",
"name": "Add self-serve question | Copenhagen Truck Wash API",
"description": "",
"image": "",
"inLanguage":"en-US"
}</script><!-- End Schema.org --><!-- Schema.org WebSite --><script type="application/ld+json">{
"@type": "WebSite",
"@id": "writerside-documentation/#website",
"url": "writerside-documentation/",
"name": "Copenhagen Truck Wash API Help"
}</script><!-- End Schema.org --></head><body data-id="addSelfserveQuestion" data-main-title="Add self-serve question" data-article-props="{&quot;seeAlsoStyle&quot;:&quot;links&quot;}" data-template="article" data-breadcrumbs="API-Reference.topic|API Reference///Tag_Self_Serve.topic|Self-Serve///Tag_Self_Serve_Page_1.topic|Self-Serve - Page 1 of 2"><div class="wrapper"><main class="panel _main"><header class="panel__header"><div class="container"><h3>Copenhagen Truck Wash API Help</h3><div class="panel-trigger"></div></div></header><section class="panel__content"><div class="container"><article class="article" data-shortcut-switcher="inactive"><h1 data-toc="addSelfserveQuestion" id="addSelfserveQuestion.topic">Add self-serve question</h1><p id="-48x1y0_2">This endpoint documentation is generated directly from <code class="code" id="-48x1y0_8">openapi.yaml</code>.</p><section class="chapter"><h2 id="endpoint" data-toc="endpoint">Endpoint</h2><div class="code-block" data-lang="http">POST /department/selfserve/questions</div></section><section class="chapter"><h2 id="operation" data-toc="operation">Operation</h2><p id="-48x1y0_10">Operation ID: <code class="code" id="-48x1y0_12">addSelfserveQuestion</code></p><p id="-48x1y0_11">Add a new self-serve question. Questions are typically shared across departments and lanes by omitting department, lane, and product, which default to 0.</p></section><section class="chapter"><h2 id="authentication" data-toc="authentication">Authentication</h2><p id="-48x1y0_13">Security requirements:</p><div class="table-wrapper"><table class="wide" id="-48x1y0_14"><thead><tr class="ijRowHead" id="-48x1y0_15"><th id="-48x1y0_17"><p>Scheme</p></th><th id="-48x1y0_18"><p>Scopes</p></th></tr></thead><tbody><tr id="-48x1y0_16"><td id="-48x1y0_19"><p>BearerAuth</p></td><td id="-48x1y0_20"><p>-</p></td></tr></tbody></table></div></section><section class="chapter"><h2 id="request-body" data-toc="request-body">Request Body</h2><p id="-48x1y0_21">Required: yes.</p><p id="-48x1y0_22">Content type: <code class="code" id="-48x1y0_24">application/json</code></p><div class="code-block" data-lang="json">
{
&quot;properties&quot;: {
&quot;condition_id&quot;: {
&quot;nullable&quot;: true,
&quot;type&quot;: &quot;integer&quot;
},
&quot;department&quot;: {
&quot;default&quot;: 0,
&quot;type&quot;: &quot;integer&quot;
},
&quot;description&quot;: {
&quot;type&quot;: &quot;string&quot;
},
&quot;lane&quot;: {
&quot;default&quot;: 0,
&quot;type&quot;: &quot;integer&quot;
},
&quot;order_priority&quot;: {
&quot;default&quot;: 0,
&quot;type&quot;: &quot;integer&quot;
},
&quot;product&quot;: {
&quot;default&quot;: 0,
&quot;type&quot;: &quot;integer&quot;
},
&quot;question&quot;: {
&quot;type&quot;: &quot;string&quot;
}
},
&quot;required&quot;: [
&quot;question&quot;,
&quot;description&quot;
],
&quot;type&quot;: &quot;object&quot;
}
</div></section><section class="chapter"><h2 id="responses" data-toc="responses">Responses</h2><div class="table-wrapper"><table class="wide" id="-48x1y0_25"><thead><tr class="ijRowHead" id="-48x1y0_28"><th id="-48x1y0_32"><p>Status</p></th><th id="-48x1y0_33"><p>Description</p></th><th id="-48x1y0_34"><p>Content Types</p></th></tr></thead><tbody><tr id="-48x1y0_29"><td id="-48x1y0_35"><p>200</p></td><td id="-48x1y0_36"><p>Successfully added question</p></td><td id="-48x1y0_37"><p>application/json</p></td></tr><tr id="-48x1y0_30"><td id="-48x1y0_38"><p>400</p></td><td id="-48x1y0_39"></td><td id="-48x1y0_40"></td></tr><tr id="-48x1y0_31"><td id="-48x1y0_41"><p>500</p></td><td id="-48x1y0_42"></td><td id="-48x1y0_43"></td></tr></tbody></table></div><p id="-48x1y0_26">Schema for response <code class="code" id="-48x1y0_44">200</code> (<code class="code" id="-48x1y0_45">application/json</code>):</p><div class="code-block" data-lang="json">
{
&quot;$ref&quot;: &quot;#/components/schemas/DepartmentSelfserveQuestion&quot;
}
</div></section><div class="last-modified">17 March 2026</div><div data-feedback-placeholder="true"></div><div class="navigation-links _bottom"><a href="listselfservequestions.html" class="navigation-links__prev">List self-serve questions</a><a href="updateselfservequestion.html" class="navigation-links__next">Update self-serve question</a></div></article><div id="disqus_thread"></div></div></section></main></div><script src="https://resources.jetbrains.com/writerside/apidoc/6.26.0-b913/app.js"></script></body></html>
@@ -0,0 +1,80 @@
<!DOCTYPE html SYSTEM "about:legacy-compat">
<html lang="en-US" data-preset="contrast" data-primary-color="#307FFF"><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8"><meta charset="UTF-8"><meta name="robots" content="noindex"><meta name="built-on" content="2026-03-17T15:38:45.855911338"><title>Add self-serve task | Copenhagen Truck Wash API</title><script type="application/json" id="virtual-toc-data">[{"id":"endpoint","level":0,"title":"Endpoint","anchor":"#endpoint"},{"id":"operation","level":0,"title":"Operation","anchor":"#operation"},{"id":"authentication","level":0,"title":"Authentication","anchor":"#authentication"},{"id":"request-body","level":0,"title":"Request Body","anchor":"#request-body"},{"id":"responses","level":0,"title":"Responses","anchor":"#responses"}]</script><script type="application/json" id="topic-shortcuts"></script><link href="https://resources.jetbrains.com/writerside/apidoc/6.26.0-b913/app.css" rel="stylesheet"><meta name="msapplication-TileColor" content="#000000"><link rel="apple-touch-icon" sizes="180x180" href="https://jetbrains.com/apple-touch-icon.png"><link rel="icon" type="image/png" sizes="32x32" href="https://jetbrains.com/favicon-32x32.png"><link rel="icon" type="image/png" sizes="16x16" href="https://jetbrains.com/favicon-16x16.png"><meta name="msapplication-TileImage" content="https://resources.jetbrains.com/storage/ui/favicons/mstile-144x144.png"><meta name="msapplication-square70x70logo" content="https://resources.jetbrains.com/storage/ui/favicons/mstile-70x70.png"><meta name="msapplication-square150x150logo" content="https://resources.jetbrains.com/storage/ui/favicons/mstile-150x150.png"><meta name="msapplication-wide310x150logo" content="https://resources.jetbrains.com/storage/ui/favicons/mstile-310x150.png"><meta name="msapplication-square310x310logo" content="https://resources.jetbrains.com/storage/ui/favicons/mstile-310x310.png"><meta name="image" content=""><!-- Open Graph --><meta property="og:title" content="Add self-serve task | Copenhagen Truck Wash API"><meta property="og:description" content=""><meta property="og:image" content=""><meta property="og:site_name" content="Copenhagen Truck Wash API Help"><meta property="og:type" content="website"><meta property="og:locale" content="en_US"><meta property="og:url" content="writerside-documentation/addselfservetask.html"><!-- End Open Graph --><!-- Twitter Card --><meta name="twitter:card" content="summary_large_image"><meta name="twitter:site" content=""><meta name="twitter:title" content="Add self-serve task | Copenhagen Truck Wash API"><meta name="twitter:description" content=""><meta name="twitter:creator" content=""><meta name="twitter:image:src" content=""><!-- End Twitter Card --><!-- Schema.org WebPage --><script type="application/ld+json">{
"@context": "http://schema.org",
"@type": "WebPage",
"@id": "writerside-documentation/addselfservetask.html#webpage",
"url": "writerside-documentation/addselfservetask.html",
"name": "Add self-serve task | Copenhagen Truck Wash API",
"description": "",
"image": "",
"inLanguage":"en-US"
}</script><!-- End Schema.org --><!-- Schema.org WebSite --><script type="application/ld+json">{
"@type": "WebSite",
"@id": "writerside-documentation/#website",
"url": "writerside-documentation/",
"name": "Copenhagen Truck Wash API Help"
}</script><!-- End Schema.org --></head><body data-id="addSelfserveTask" data-main-title="Add self-serve task" data-article-props="{&quot;seeAlsoStyle&quot;:&quot;links&quot;}" data-template="article" data-breadcrumbs="API-Reference.topic|API Reference///Tag_Self_Serve.topic|Self-Serve///Tag_Self_Serve_Page_1.topic|Self-Serve - Page 1 of 2"><div class="wrapper"><main class="panel _main"><header class="panel__header"><div class="container"><h3>Copenhagen Truck Wash API Help</h3><div class="panel-trigger"></div></div></header><section class="panel__content"><div class="container"><article class="article" data-shortcut-switcher="inactive"><h1 data-toc="addSelfserveTask" id="addSelfserveTask.topic">Add self-serve task</h1><p id="-z0wiex_2">This endpoint documentation is generated directly from <code class="code" id="-z0wiex_8">openapi.yaml</code>.</p><section class="chapter"><h2 id="endpoint" data-toc="endpoint">Endpoint</h2><div class="code-block" data-lang="http">POST /department/selfserve/tasks</div></section><section class="chapter"><h2 id="operation" data-toc="operation">Operation</h2><p id="-z0wiex_10">Operation ID: <code class="code" id="-z0wiex_12">addSelfserveTask</code></p><p id="-z0wiex_11">Add a new self-serve task. Either provide a reusable machine_type_id or a legacy department/lane/product scope.</p></section><section class="chapter"><h2 id="authentication" data-toc="authentication">Authentication</h2><p id="-z0wiex_13">Security requirements:</p><div class="table-wrapper"><table class="wide" id="-z0wiex_14"><thead><tr class="ijRowHead" id="-z0wiex_15"><th id="-z0wiex_17"><p>Scheme</p></th><th id="-z0wiex_18"><p>Scopes</p></th></tr></thead><tbody><tr id="-z0wiex_16"><td id="-z0wiex_19"><p>BearerAuth</p></td><td id="-z0wiex_20"><p>-</p></td></tr></tbody></table></div></section><section class="chapter"><h2 id="request-body" data-toc="request-body">Request Body</h2><p id="-z0wiex_21">Required: yes.</p><p id="-z0wiex_22">Content type: <code class="code" id="-z0wiex_24">application/json</code></p><div class="code-block" data-lang="json">
{
&quot;properties&quot;: {
&quot;buttons&quot;: {
&quot;default&quot;: [],
&quot;description&quot;: &quot;Optional dynamic image button IDs enabled by this task.&quot;,
&quot;items&quot;: {
&quot;type&quot;: &quot;integer&quot;
},
&quot;type&quot;: &quot;array&quot;
},
&quot;condition_id&quot;: {
&quot;nullable&quot;: true,
&quot;type&quot;: &quot;integer&quot;
},
&quot;department&quot;: {
&quot;default&quot;: 0,
&quot;type&quot;: &quot;integer&quot;
},
&quot;description&quot;: {
&quot;type&quot;: &quot;string&quot;
},
&quot;dynamic_images_vehicle_type&quot;: {
&quot;description&quot;: &quot;Optional vehicle type selection override for the machine UI. Integer &gt;= 0 or null.&quot;,
&quot;nullable&quot;: true,
&quot;type&quot;: &quot;integer&quot;
},
&quot;lane&quot;: {
&quot;default&quot;: 0,
&quot;type&quot;: &quot;integer&quot;
},
&quot;machine_type_id&quot;: {
&quot;nullable&quot;: true,
&quot;type&quot;: &quot;integer&quot;
},
&quot;order_priority&quot;: {
&quot;default&quot;: 0,
&quot;type&quot;: &quot;integer&quot;
},
&quot;product&quot;: {
&quot;default&quot;: 0,
&quot;type&quot;: &quot;integer&quot;
},
&quot;services&quot;: {
&quot;description&quot;: &quot;Optional services enabled by this task. Items must be valid service enum names.&quot;,
&quot;items&quot;: {
&quot;$ref&quot;: &quot;#/components/schemas/SelfserveLaneService&quot;
},
&quot;type&quot;: &quot;array&quot;
},
&quot;task&quot;: {
&quot;type&quot;: &quot;string&quot;
}
},
&quot;required&quot;: [
&quot;task&quot;,
&quot;description&quot;
],
&quot;type&quot;: &quot;object&quot;
}
</div></section><section class="chapter"><h2 id="responses" data-toc="responses">Responses</h2><div class="table-wrapper"><table class="wide" id="-z0wiex_25"><thead><tr class="ijRowHead" id="-z0wiex_28"><th id="-z0wiex_32"><p>Status</p></th><th id="-z0wiex_33"><p>Description</p></th><th id="-z0wiex_34"><p>Content Types</p></th></tr></thead><tbody><tr id="-z0wiex_29"><td id="-z0wiex_35"><p>200</p></td><td id="-z0wiex_36"><p>Successfully added task</p></td><td id="-z0wiex_37"><p>application/json</p></td></tr><tr id="-z0wiex_30"><td id="-z0wiex_38"><p>400</p></td><td id="-z0wiex_39"></td><td id="-z0wiex_40"></td></tr><tr id="-z0wiex_31"><td id="-z0wiex_41"><p>500</p></td><td id="-z0wiex_42"></td><td id="-z0wiex_43"></td></tr></tbody></table></div><p id="-z0wiex_26">Schema for response <code class="code" id="-z0wiex_44">200</code> (<code class="code" id="-z0wiex_45">application/json</code>):</p><div class="code-block" data-lang="json">
{
&quot;$ref&quot;: &quot;#/components/schemas/DepartmentSelfserveTask&quot;
}
</div></section><div class="last-modified">17 March 2026</div><div data-feedback-placeholder="true"></div><div class="navigation-links _bottom"><a href="listselfservetasks.html" class="navigation-links__prev">List self-serve tasks</a><a href="updateselfservetask.html" class="navigation-links__next">Update self-serve task</a></div></article><div id="disqus_thread"></div></div></section></main></div><script src="https://resources.jetbrains.com/writerside/apidoc/6.26.0-b913/app.js"></script></body></html>
@@ -0,0 +1,51 @@
<!DOCTYPE html SYSTEM "about:legacy-compat">
<html lang="en-US" data-preset="contrast" data-primary-color="#307FFF"><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8"><meta charset="UTF-8"><meta name="robots" content="noindex"><meta name="built-on" content="2026-03-17T15:41:57.83478546"><title>Add vehicle condition | Copenhagen Truck Wash API</title><script type="application/json" id="virtual-toc-data">[{"id":"endpoint","level":0,"title":"Endpoint","anchor":"#endpoint"},{"id":"operation","level":0,"title":"Operation","anchor":"#operation"},{"id":"authentication","level":0,"title":"Authentication","anchor":"#authentication"},{"id":"request-body","level":0,"title":"Request Body","anchor":"#request-body"},{"id":"responses","level":0,"title":"Responses","anchor":"#responses"}]</script><script type="application/json" id="topic-shortcuts"></script><link href="https://resources.jetbrains.com/writerside/apidoc/6.26.0-b913/app.css" rel="stylesheet"><meta name="msapplication-TileColor" content="#000000"><link rel="apple-touch-icon" sizes="180x180" href="https://jetbrains.com/apple-touch-icon.png"><link rel="icon" type="image/png" sizes="32x32" href="https://jetbrains.com/favicon-32x32.png"><link rel="icon" type="image/png" sizes="16x16" href="https://jetbrains.com/favicon-16x16.png"><meta name="msapplication-TileImage" content="https://resources.jetbrains.com/storage/ui/favicons/mstile-144x144.png"><meta name="msapplication-square70x70logo" content="https://resources.jetbrains.com/storage/ui/favicons/mstile-70x70.png"><meta name="msapplication-square150x150logo" content="https://resources.jetbrains.com/storage/ui/favicons/mstile-150x150.png"><meta name="msapplication-wide310x150logo" content="https://resources.jetbrains.com/storage/ui/favicons/mstile-310x150.png"><meta name="msapplication-square310x310logo" content="https://resources.jetbrains.com/storage/ui/favicons/mstile-310x310.png"><meta name="image" content=""><!-- Open Graph --><meta property="og:title" content="Add vehicle condition | Copenhagen Truck Wash API"><meta property="og:description" content=""><meta property="og:image" content=""><meta property="og:site_name" content="Copenhagen Truck Wash API Help"><meta property="og:type" content="website"><meta property="og:locale" content="en_US"><meta property="og:url" content="writerside-documentation/addselfservevehiclecondition.html"><!-- End Open Graph --><!-- Twitter Card --><meta name="twitter:card" content="summary_large_image"><meta name="twitter:site" content=""><meta name="twitter:title" content="Add vehicle condition | Copenhagen Truck Wash API"><meta name="twitter:description" content=""><meta name="twitter:creator" content=""><meta name="twitter:image:src" content=""><!-- End Twitter Card --><!-- Schema.org WebPage --><script type="application/ld+json">{
"@context": "http://schema.org",
"@type": "WebPage",
"@id": "writerside-documentation/addselfservevehiclecondition.html#webpage",
"url": "writerside-documentation/addselfservevehiclecondition.html",
"name": "Add vehicle condition | Copenhagen Truck Wash API",
"description": "",
"image": "",
"inLanguage":"en-US"
}</script><!-- End Schema.org --><!-- Schema.org WebSite --><script type="application/ld+json">{
"@type": "WebSite",
"@id": "writerside-documentation/#website",
"url": "writerside-documentation/",
"name": "Copenhagen Truck Wash API Help"
}</script><!-- End Schema.org --></head><body data-id="addSelfserveVehicleCondition" data-main-title="Add vehicle condition" data-article-props="{&quot;seeAlsoStyle&quot;:&quot;links&quot;}" data-template="article" data-breadcrumbs="API-Reference.topic|API Reference///Tag_Self_Serve.topic|Self-Serve///Tag_Self_Serve_Page_2.topic|Self-Serve - Page 2 of 2"><div class="wrapper"><main class="panel _main"><header class="panel__header"><div class="container"><h3>Copenhagen Truck Wash API Help</h3><div class="panel-trigger"></div></div></header><section class="panel__content"><div class="container"><article class="article" data-shortcut-switcher="inactive"><h1 data-toc="addSelfserveVehicleCondition" id="addSelfserveVehicleCondition.topic">Add vehicle condition</h1><p id="c9k51t_2">This endpoint documentation is generated directly from <code class="code" id="c9k51t_8">openapi.yaml</code>.</p><section class="chapter"><h2 id="endpoint" data-toc="endpoint">Endpoint</h2><div class="code-block" data-lang="http">POST /department/selfserve/vehicle/conditions</div></section><section class="chapter"><h2 id="operation" data-toc="operation">Operation</h2><p id="c9k51t_10">Operation ID: <code class="code" id="c9k51t_12">addSelfserveVehicleCondition</code></p><p id="c9k51t_11">Add a new vehicle condition (answer to a question). Customers can only add conditions for their own vehicles.</p></section><section class="chapter"><h2 id="authentication" data-toc="authentication">Authentication</h2><p id="c9k51t_13">Security requirements:</p><div class="table-wrapper"><table class="wide" id="c9k51t_14"><thead><tr class="ijRowHead" id="c9k51t_15"><th id="c9k51t_17"><p>Scheme</p></th><th id="c9k51t_18"><p>Scopes</p></th></tr></thead><tbody><tr id="c9k51t_16"><td id="c9k51t_19"><p>BearerAuth</p></td><td id="c9k51t_20"><p>-</p></td></tr></tbody></table></div></section><section class="chapter"><h2 id="request-body" data-toc="request-body">Request Body</h2><p id="c9k51t_21">Required: yes.</p><p id="c9k51t_22">Content type: <code class="code" id="c9k51t_24">application/json</code></p><div class="code-block" data-lang="json">
{
&quot;properties&quot;: {
&quot;customer_id&quot;: {
&quot;nullable&quot;: true,
&quot;type&quot;: &quot;integer&quot;
},
&quot;department&quot;: {
&quot;type&quot;: &quot;integer&quot;
},
&quot;lane&quot;: {
&quot;type&quot;: &quot;integer&quot;
},
&quot;question&quot;: {
&quot;type&quot;: &quot;integer&quot;
},
&quot;reg&quot;: {
&quot;type&quot;: &quot;string&quot;
},
&quot;value&quot;: {
&quot;type&quot;: &quot;boolean&quot;
}
},
&quot;required&quot;: [
&quot;department&quot;,
&quot;lane&quot;,
&quot;reg&quot;,
&quot;question&quot;
],
&quot;type&quot;: &quot;object&quot;
}
</div></section><section class="chapter"><h2 id="responses" data-toc="responses">Responses</h2><div class="table-wrapper"><table class="wide" id="c9k51t_25"><thead><tr class="ijRowHead" id="c9k51t_28"><th id="c9k51t_32"><p>Status</p></th><th id="c9k51t_33"><p>Description</p></th><th id="c9k51t_34"><p>Content Types</p></th></tr></thead><tbody><tr id="c9k51t_29"><td id="c9k51t_35"><p>200</p></td><td id="c9k51t_36"><p>Successfully added vehicle condition</p></td><td id="c9k51t_37"><p>application/json</p></td></tr><tr id="c9k51t_30"><td id="c9k51t_38"><p>400</p></td><td id="c9k51t_39"></td><td id="c9k51t_40"></td></tr><tr id="c9k51t_31"><td id="c9k51t_41"><p>500</p></td><td id="c9k51t_42"></td><td id="c9k51t_43"></td></tr></tbody></table></div><p id="c9k51t_26">Schema for response <code class="code" id="c9k51t_44">200</code> (<code class="code" id="c9k51t_45">application/json</code>):</p><div class="code-block" data-lang="json">
{
&quot;$ref&quot;: &quot;#/components/schemas/DepartmentSelfserveVehicleConditionMutationResponse&quot;
}
</div></section><div class="last-modified">17 March 2026</div><div data-feedback-placeholder="true"></div><div class="navigation-links _bottom"><a href="listselfservevehicleconditions.html" class="navigation-links__prev">List vehicle conditions</a><a href="updateselfservevehiclecondition.html" class="navigation-links__next">Update vehicle condition</a></div></article><div id="disqus_thread"></div></div></section></main></div><script src="https://resources.jetbrains.com/writerside/apidoc/6.26.0-b913/app.js"></script></body></html>
@@ -0,0 +1,51 @@
<!DOCTYPE html SYSTEM "about:legacy-compat">
<html lang="en-US" data-preset="contrast" data-primary-color="#307FFF"><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8"><meta charset="UTF-8"><meta name="robots" content="noindex"><meta name="built-on" content="2026-03-17T15:40:12.090036324"><title>Add vehicle | Copenhagen Truck Wash API</title><script type="application/json" id="virtual-toc-data">[{"id":"endpoint","level":0,"title":"Endpoint","anchor":"#endpoint"},{"id":"operation","level":0,"title":"Operation","anchor":"#operation"},{"id":"authentication","level":0,"title":"Authentication","anchor":"#authentication"},{"id":"parameters","level":0,"title":"Parameters","anchor":"#parameters"},{"id":"request-body","level":0,"title":"Request Body","anchor":"#request-body"},{"id":"responses","level":0,"title":"Responses","anchor":"#responses"}]</script><script type="application/json" id="topic-shortcuts"></script><link href="https://resources.jetbrains.com/writerside/apidoc/6.26.0-b913/app.css" rel="stylesheet"><meta name="msapplication-TileColor" content="#000000"><link rel="apple-touch-icon" sizes="180x180" href="https://jetbrains.com/apple-touch-icon.png"><link rel="icon" type="image/png" sizes="32x32" href="https://jetbrains.com/favicon-32x32.png"><link rel="icon" type="image/png" sizes="16x16" href="https://jetbrains.com/favicon-16x16.png"><meta name="msapplication-TileImage" content="https://resources.jetbrains.com/storage/ui/favicons/mstile-144x144.png"><meta name="msapplication-square70x70logo" content="https://resources.jetbrains.com/storage/ui/favicons/mstile-70x70.png"><meta name="msapplication-square150x150logo" content="https://resources.jetbrains.com/storage/ui/favicons/mstile-150x150.png"><meta name="msapplication-wide310x150logo" content="https://resources.jetbrains.com/storage/ui/favicons/mstile-310x150.png"><meta name="msapplication-square310x310logo" content="https://resources.jetbrains.com/storage/ui/favicons/mstile-310x310.png"><meta name="image" content=""><!-- Open Graph --><meta property="og:title" content="Add vehicle | Copenhagen Truck Wash API"><meta property="og:description" content=""><meta property="og:image" content=""><meta property="og:site_name" content="Copenhagen Truck Wash API Help"><meta property="og:type" content="website"><meta property="og:locale" content="en_US"><meta property="og:url" content="writerside-documentation/addvehicle.html"><!-- End Open Graph --><!-- Twitter Card --><meta name="twitter:card" content="summary_large_image"><meta name="twitter:site" content=""><meta name="twitter:title" content="Add vehicle | Copenhagen Truck Wash API"><meta name="twitter:description" content=""><meta name="twitter:creator" content=""><meta name="twitter:image:src" content=""><!-- End Twitter Card --><!-- Schema.org WebPage --><script type="application/ld+json">{
"@context": "http://schema.org",
"@type": "WebPage",
"@id": "writerside-documentation/addvehicle.html#webpage",
"url": "writerside-documentation/addvehicle.html",
"name": "Add vehicle | Copenhagen Truck Wash API",
"description": "",
"image": "",
"inLanguage":"en-US"
}</script><!-- End Schema.org --><!-- Schema.org WebSite --><script type="application/ld+json">{
"@type": "WebSite",
"@id": "writerside-documentation/#website",
"url": "writerside-documentation/",
"name": "Copenhagen Truck Wash API Help"
}</script><!-- End Schema.org --></head><body data-id="addVehicle" data-main-title="Add vehicle" data-article-props="{&quot;seeAlsoStyle&quot;:&quot;links&quot;}" data-template="article" data-breadcrumbs="API-Reference.topic|API Reference///Tag_Vehicles.topic|Vehicles///Tag_Vehicles_Page_1.topic|Vehicles - Page 1 of 1"><div class="wrapper"><main class="panel _main"><header class="panel__header"><div class="container"><h3>Copenhagen Truck Wash API Help</h3><div class="panel-trigger"></div></div></header><section class="panel__content"><div class="container"><article class="article" data-shortcut-switcher="inactive"><h1 data-toc="addVehicle" id="addVehicle.topic">Add vehicle</h1><p id="-59dur9_2">This endpoint documentation is generated directly from <code class="code" id="-59dur9_9">openapi.yaml</code>.</p><section class="chapter"><h2 id="endpoint" data-toc="endpoint">Endpoint</h2><div class="code-block" data-lang="http">POST /vehicles</div></section><section class="chapter"><h2 id="operation" data-toc="operation">Operation</h2><p id="-59dur9_11">Operation ID: <code class="code" id="-59dur9_13">addVehicle</code></p><p id="-59dur9_12">Create a new vehicle for a customer. Permissions: - Own scope: `add_vehicle` (linked to subuser node `VEHICLES_ADD`). - Broader scope: `add_vehicle_other`.</p></section><section class="chapter"><h2 id="authentication" data-toc="authentication">Authentication</h2><p id="-59dur9_14">Security requirements:</p><div class="table-wrapper"><table class="wide" id="-59dur9_15"><thead><tr class="ijRowHead" id="-59dur9_16"><th id="-59dur9_18"><p>Scheme</p></th><th id="-59dur9_19"><p>Scopes</p></th></tr></thead><tbody><tr id="-59dur9_17"><td id="-59dur9_20"><p>BearerAuth</p></td><td id="-59dur9_21"><p>-</p></td></tr></tbody></table></div></section><section class="chapter"><h2 id="parameters" data-toc="parameters">Parameters</h2><div class="table-wrapper"><table class="wide" id="-59dur9_22"><thead><tr class="ijRowHead" id="-59dur9_23"><th id="-59dur9_25"><p>Name</p></th><th id="-59dur9_26"><p>In</p></th><th id="-59dur9_27"><p>Required</p></th><th id="-59dur9_28"><p>Type</p></th><th id="-59dur9_29"><p>Description</p></th></tr></thead><tbody><tr id="-59dur9_24"><td id="-59dur9_30"></td><td id="-59dur9_31"></td><td id="-59dur9_32"><p>no</p></td><td id="-59dur9_33"><p>object</p></td><td id="-59dur9_34"></td></tr></tbody></table></div></section><section class="chapter"><h2 id="request-body" data-toc="request-body">Request Body</h2><p id="-59dur9_35">Required: yes.</p><p id="-59dur9_36">Content type: <code class="code" id="-59dur9_38">application/json</code></p><div class="code-block" data-lang="json">
{
&quot;properties&quot;: {
&quot;customer_id&quot;: {
&quot;description&quot;: &quot;Optional explicit target customer. Defaults to the effective customer context.&quot;,
&quot;type&quot;: &quot;integer&quot;
},
&quot;reference&quot;: {
&quot;maxLength&quot;: 255,
&quot;nullable&quot;: true,
&quot;type&quot;: &quot;string&quot;
},
&quot;reg&quot;: {
&quot;description&quot;: &quot;Vehicle registration number&quot;,
&quot;maxLength&quot;: 12,
&quot;minLength&quot;: 2,
&quot;type&quot;: &quot;string&quot;
},
&quot;type&quot;: {
&quot;description&quot;: &quot;Product ID representing the vehicle wash type&quot;,
&quot;type&quot;: &quot;integer&quot;
},
&quot;wash_subscription&quot;: {
&quot;type&quot;: &quot;boolean&quot;
}
},
&quot;required&quot;: [
&quot;reg&quot;,
&quot;type&quot;,
&quot;wash_subscription&quot;
],
&quot;type&quot;: &quot;object&quot;
}
</div></section><section class="chapter"><h2 id="responses" data-toc="responses">Responses</h2><div class="table-wrapper"><table class="wide" id="-59dur9_39"><thead><tr class="ijRowHead" id="-59dur9_42"><th id="-59dur9_46"><p>Status</p></th><th id="-59dur9_47"><p>Description</p></th><th id="-59dur9_48"><p>Content Types</p></th></tr></thead><tbody><tr id="-59dur9_43"><td id="-59dur9_49"><p>200</p></td><td id="-59dur9_50"><p>Vehicle created</p></td><td id="-59dur9_51"><p>application/json</p></td></tr><tr id="-59dur9_44"><td id="-59dur9_52"><p>400</p></td><td id="-59dur9_53"></td><td id="-59dur9_54"></td></tr><tr id="-59dur9_45"><td id="-59dur9_55"><p>403</p></td><td id="-59dur9_56"></td><td id="-59dur9_57"></td></tr></tbody></table></div><p id="-59dur9_40">Schema for response <code class="code" id="-59dur9_58">200</code> (<code class="code" id="-59dur9_59">application/json</code>):</p><div class="code-block" data-lang="json">
{}
</div></section><div class="last-modified">17 March 2026</div><div data-feedback-placeholder="true"></div><div class="navigation-links _bottom"><a href="listvehicles.html" class="navigation-links__prev">List vehicles</a><a href="editvehicle.html" class="navigation-links__next">Edit vehicle</a></div></article><div id="disqus_thread"></div></div></section></main></div><script src="https://resources.jetbrains.com/writerside/apidoc/6.26.0-b913/app.js"></script></body></html>
@@ -0,0 +1,30 @@
<!DOCTYPE html SYSTEM "about:legacy-compat">
<html lang="en-US" data-preset="contrast" data-primary-color="#307FFF"><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8"><meta charset="UTF-8"><meta name="robots" content="noindex"><meta name="built-on" content="2026-03-17T15:39:51.307874295"><title>Delete booking (admin) | Copenhagen Truck Wash API</title><script type="application/json" id="virtual-toc-data">[{"id":"endpoint","level":0,"title":"Endpoint","anchor":"#endpoint"},{"id":"operation","level":0,"title":"Operation","anchor":"#operation"},{"id":"authentication","level":0,"title":"Authentication","anchor":"#authentication"},{"id":"request-body","level":0,"title":"Request Body","anchor":"#request-body"},{"id":"responses","level":0,"title":"Responses","anchor":"#responses"}]</script><script type="application/json" id="topic-shortcuts"></script><link href="https://resources.jetbrains.com/writerside/apidoc/6.26.0-b913/app.css" rel="stylesheet"><meta name="msapplication-TileColor" content="#000000"><link rel="apple-touch-icon" sizes="180x180" href="https://jetbrains.com/apple-touch-icon.png"><link rel="icon" type="image/png" sizes="32x32" href="https://jetbrains.com/favicon-32x32.png"><link rel="icon" type="image/png" sizes="16x16" href="https://jetbrains.com/favicon-16x16.png"><meta name="msapplication-TileImage" content="https://resources.jetbrains.com/storage/ui/favicons/mstile-144x144.png"><meta name="msapplication-square70x70logo" content="https://resources.jetbrains.com/storage/ui/favicons/mstile-70x70.png"><meta name="msapplication-square150x150logo" content="https://resources.jetbrains.com/storage/ui/favicons/mstile-150x150.png"><meta name="msapplication-wide310x150logo" content="https://resources.jetbrains.com/storage/ui/favicons/mstile-310x150.png"><meta name="msapplication-square310x310logo" content="https://resources.jetbrains.com/storage/ui/favicons/mstile-310x310.png"><meta name="image" content=""><!-- Open Graph --><meta property="og:title" content="Delete booking (admin) | Copenhagen Truck Wash API"><meta property="og:description" content=""><meta property="og:image" content=""><meta property="og:site_name" content="Copenhagen Truck Wash API Help"><meta property="og:type" content="website"><meta property="og:locale" content="en_US"><meta property="og:url" content="writerside-documentation/admindeletebooking.html"><!-- End Open Graph --><!-- Twitter Card --><meta name="twitter:card" content="summary_large_image"><meta name="twitter:site" content=""><meta name="twitter:title" content="Delete booking (admin) | Copenhagen Truck Wash API"><meta name="twitter:description" content=""><meta name="twitter:creator" content=""><meta name="twitter:image:src" content=""><!-- End Twitter Card --><!-- Schema.org WebPage --><script type="application/ld+json">{
"@context": "http://schema.org",
"@type": "WebPage",
"@id": "writerside-documentation/admindeletebooking.html#webpage",
"url": "writerside-documentation/admindeletebooking.html",
"name": "Delete booking (admin) | Copenhagen Truck Wash API",
"description": "",
"image": "",
"inLanguage":"en-US"
}</script><!-- End Schema.org --><!-- Schema.org WebSite --><script type="application/ld+json">{
"@type": "WebSite",
"@id": "writerside-documentation/#website",
"url": "writerside-documentation/",
"name": "Copenhagen Truck Wash API Help"
}</script><!-- End Schema.org --></head><body data-id="adminDeleteBooking" data-main-title="Delete booking (admin)" data-article-props="{&quot;seeAlsoStyle&quot;:&quot;links&quot;}" data-template="article" data-breadcrumbs="API-Reference.topic|API Reference///Tag_Bookings.topic|Bookings///Tag_Bookings_Page_1.topic|Bookings - Page 1 of 1"><div class="wrapper"><main class="panel _main"><header class="panel__header"><div class="container"><h3>Copenhagen Truck Wash API Help</h3><div class="panel-trigger"></div></div></header><section class="panel__content"><div class="container"><article class="article" data-shortcut-switcher="inactive"><h1 data-toc="adminDeleteBooking" id="adminDeleteBooking.topic">Delete booking (admin)</h1><p id="z68no4f_2">This endpoint documentation is generated directly from <code class="code" id="z68no4f_8">openapi.yaml</code>.</p><section class="chapter"><h2 id="endpoint" data-toc="endpoint">Endpoint</h2><div class="code-block" data-lang="http">POST /admin/bookings/delete</div></section><section class="chapter"><h2 id="operation" data-toc="operation">Operation</h2><p id="z68no4f_10">Operation ID: <code class="code" id="z68no4f_12">adminDeleteBooking</code></p><p id="z68no4f_11">Delete booking (admin)</p></section><section class="chapter"><h2 id="authentication" data-toc="authentication">Authentication</h2><p id="z68no4f_13">Security requirements:</p><div class="table-wrapper"><table class="wide" id="z68no4f_14"><thead><tr class="ijRowHead" id="z68no4f_15"><th id="z68no4f_17"><p>Scheme</p></th><th id="z68no4f_18"><p>Scopes</p></th></tr></thead><tbody><tr id="z68no4f_16"><td id="z68no4f_19"><p>BearerAuth</p></td><td id="z68no4f_20"><p>-</p></td></tr></tbody></table></div></section><section class="chapter"><h2 id="request-body" data-toc="request-body">Request Body</h2><p id="z68no4f_21">Required: yes.</p><p id="z68no4f_22">Content type: <code class="code" id="z68no4f_24">application/json</code></p><div class="code-block" data-lang="json">
{
&quot;properties&quot;: {
&quot;id&quot;: {
&quot;type&quot;: &quot;integer&quot;
}
},
&quot;required&quot;: [
&quot;id&quot;
],
&quot;type&quot;: &quot;object&quot;
}
</div></section><section class="chapter"><h2 id="responses" data-toc="responses">Responses</h2><div class="table-wrapper"><table class="wide" id="z68no4f_25"><thead><tr class="ijRowHead" id="z68no4f_28"><th id="z68no4f_30"><p>Status</p></th><th id="z68no4f_31"><p>Description</p></th><th id="z68no4f_32"><p>Content Types</p></th></tr></thead><tbody><tr id="z68no4f_29"><td id="z68no4f_33"><p>200</p></td><td id="z68no4f_34"><p>Success</p></td><td id="z68no4f_35"><p>application/json</p></td></tr></tbody></table></div><p id="z68no4f_26">Schema for response <code class="code" id="z68no4f_36">200</code> (<code class="code" id="z68no4f_37">application/json</code>):</p><div class="code-block" data-lang="json">
{}
</div></section><div class="last-modified">17 March 2026</div><div data-feedback-placeholder="true"></div><div class="navigation-links _bottom"><a href="completewashwithoutwashcertificate.html" class="navigation-links__prev">Complete wash without wash certificate</a><a href="getdepartmentbookingcount.html" class="navigation-links__next">Get department unfulfilled bookings count</a></div></article><div id="disqus_thread"></div></div></section></main></div><script src="https://resources.jetbrains.com/writerside/apidoc/6.26.0-b913/app.js"></script></body></html>
@@ -0,0 +1 @@
{}
@@ -0,0 +1,20 @@
<!DOCTYPE html SYSTEM "about:legacy-compat">
<html lang="en-US" data-preset="contrast" data-primary-color="#307FFF"><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8"><meta charset="UTF-8"><meta name="robots" content="noindex"><meta name="built-on" content="2026-03-17T15:38:08.113413637"><title>API Overview | Copenhagen Truck Wash API</title><script type="application/json" id="virtual-toc-data">[{"id":"base-url","level":0,"title":"Base URLs","anchor":"#base-url"},{"id":"common-headers","level":0,"title":"Common Headers","anchor":"#common-headers"}]</script><script type="application/json" id="topic-shortcuts"></script><link href="https://resources.jetbrains.com/writerside/apidoc/6.26.0-b913/app.css" rel="stylesheet"><meta name="msapplication-TileColor" content="#000000"><link rel="apple-touch-icon" sizes="180x180" href="https://jetbrains.com/apple-touch-icon.png"><link rel="icon" type="image/png" sizes="32x32" href="https://jetbrains.com/favicon-32x32.png"><link rel="icon" type="image/png" sizes="16x16" href="https://jetbrains.com/favicon-16x16.png"><meta name="msapplication-TileImage" content="https://resources.jetbrains.com/storage/ui/favicons/mstile-144x144.png"><meta name="msapplication-square70x70logo" content="https://resources.jetbrains.com/storage/ui/favicons/mstile-70x70.png"><meta name="msapplication-square150x150logo" content="https://resources.jetbrains.com/storage/ui/favicons/mstile-150x150.png"><meta name="msapplication-wide310x150logo" content="https://resources.jetbrains.com/storage/ui/favicons/mstile-310x150.png"><meta name="msapplication-square310x310logo" content="https://resources.jetbrains.com/storage/ui/favicons/mstile-310x310.png"><meta name="image" content=""><!-- Open Graph --><meta property="og:title" content="API Overview | Copenhagen Truck Wash API"><meta property="og:description" content=""><meta property="og:image" content=""><meta property="og:site_name" content="Copenhagen Truck Wash API Help"><meta property="og:type" content="website"><meta property="og:locale" content="en_US"><meta property="og:url" content="writerside-documentation/api-overview.html"><!-- End Open Graph --><!-- Twitter Card --><meta name="twitter:card" content="summary_large_image"><meta name="twitter:site" content=""><meta name="twitter:title" content="API Overview | Copenhagen Truck Wash API"><meta name="twitter:description" content=""><meta name="twitter:creator" content=""><meta name="twitter:image:src" content=""><!-- End Twitter Card --><!-- Schema.org WebPage --><script type="application/ld+json">{
"@context": "http://schema.org",
"@type": "WebPage",
"@id": "writerside-documentation/api-overview.html#webpage",
"url": "writerside-documentation/api-overview.html",
"name": "API Overview | Copenhagen Truck Wash API",
"description": "",
"image": "",
"inLanguage":"en-US"
}</script><!-- End Schema.org --><!-- Schema.org WebSite --><script type="application/ld+json">{
"@type": "WebSite",
"@id": "writerside-documentation/#website",
"url": "writerside-documentation/",
"name": "Copenhagen Truck Wash API Help"
}</script><!-- End Schema.org --></head><body data-id="API-Overview" data-main-title="API Overview" data-article-props="{&quot;seeAlsoStyle&quot;:&quot;links&quot;}" data-template="article" data-breadcrumbs=""><div class="wrapper"><main class="panel _main"><header class="panel__header"><div class="container"><h3>Copenhagen Truck Wash API Help</h3><div class="panel-trigger"></div></div></header><section class="panel__content"><div class="container"><article class="article" data-shortcut-switcher="inactive"><h1 data-toc="API-Overview" id="API-Overview.topic">API Overview</h1><p id="qiufyk_2">This section provides a high-level overview of how to interact with the API based on <code class="code" id="qiufyk_5">openapi.yaml</code>.</p><section class="chapter"><h2 id="base-url" data-toc="base-url">Base URLs</h2><p id="qiufyk_6">The API currently defines the following servers:</p><div class="code-block" data-lang="none">
https://api.truckwash.dk
https://api.truckwash.io
http://localhost/api
</div></section><section class="chapter"><h2 id="common-headers" data-toc="common-headers">Common Headers</h2><div class="table-wrapper"><table class="wide" id="qiufyk_8"><thead><tr class="ijRowHead" id="qiufyk_9"><th id="qiufyk_14"><p>Header</p></th><th id="qiufyk_15"><p>Description</p></th></tr></thead><tbody><tr id="qiufyk_10"><td id="qiufyk_16"><p>Authorization</p></td><td id="qiufyk_17"><p>Use Bearer authentication for protected endpoints: <code class="code" id="qiufyk_18">Authorization: Bearer &lt;JWT&gt;</code>.</p></td></tr><tr id="qiufyk_11"><td id="qiufyk_19"><p>Accept</p></td><td id="qiufyk_20"><p>Use <code class="code" id="qiufyk_21">application/json</code>.</p></td></tr><tr id="qiufyk_12"><td id="qiufyk_22"><p>Content-Type</p></td><td id="qiufyk_23"><p>Use <code class="code" id="qiufyk_24">application/json</code> for request bodies.</p></td></tr><tr id="qiufyk_13"><td id="qiufyk_25"><p>X-Customer-Number</p></td><td id="qiufyk_26"><p>Required for many customer-scoped requests when authenticated as a subuser.</p></td></tr></tbody></table></div></section><div class="last-modified">17 March 2026</div><div data-feedback-placeholder="true"></div><div class="navigation-links _bottom"><a href="introduction.html" class="navigation-links__prev">Introduction</a><a href="authentication.html" class="navigation-links__next">Authentication</a></div></article><div id="disqus_thread"></div></div></section></main></div><script src="https://resources.jetbrains.com/writerside/apidoc/6.26.0-b913/app.js"></script></body></html>
@@ -0,0 +1,16 @@
<!DOCTYPE html SYSTEM "about:legacy-compat">
<html lang="en-US" data-preset="contrast" data-primary-color="#307FFF"><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8"><meta charset="UTF-8"><meta name="robots" content="noindex"><meta name="built-on" content="2026-03-17T15:38:12.258652205"><title>API Reference | Copenhagen Truck Wash API</title><script type="application/json" id="virtual-toc-data">[]</script><script type="application/json" id="topic-shortcuts"></script><link href="https://resources.jetbrains.com/writerside/apidoc/6.26.0-b913/app.css" rel="stylesheet"><meta name="msapplication-TileColor" content="#000000"><link rel="apple-touch-icon" sizes="180x180" href="https://jetbrains.com/apple-touch-icon.png"><link rel="icon" type="image/png" sizes="32x32" href="https://jetbrains.com/favicon-32x32.png"><link rel="icon" type="image/png" sizes="16x16" href="https://jetbrains.com/favicon-16x16.png"><meta name="msapplication-TileImage" content="https://resources.jetbrains.com/storage/ui/favicons/mstile-144x144.png"><meta name="msapplication-square70x70logo" content="https://resources.jetbrains.com/storage/ui/favicons/mstile-70x70.png"><meta name="msapplication-square150x150logo" content="https://resources.jetbrains.com/storage/ui/favicons/mstile-150x150.png"><meta name="msapplication-wide310x150logo" content="https://resources.jetbrains.com/storage/ui/favicons/mstile-310x150.png"><meta name="msapplication-square310x310logo" content="https://resources.jetbrains.com/storage/ui/favicons/mstile-310x310.png"><meta name="image" content=""><!-- Open Graph --><meta property="og:title" content="API Reference | Copenhagen Truck Wash API"><meta property="og:description" content=""><meta property="og:image" content=""><meta property="og:site_name" content="Copenhagen Truck Wash API Help"><meta property="og:type" content="website"><meta property="og:locale" content="en_US"><meta property="og:url" content="writerside-documentation/api-reference.html"><!-- End Open Graph --><!-- Twitter Card --><meta name="twitter:card" content="summary_large_image"><meta name="twitter:site" content=""><meta name="twitter:title" content="API Reference | Copenhagen Truck Wash API"><meta name="twitter:description" content=""><meta name="twitter:creator" content=""><meta name="twitter:image:src" content=""><!-- End Twitter Card --><!-- Schema.org WebPage --><script type="application/ld+json">{
"@context": "http://schema.org",
"@type": "WebPage",
"@id": "writerside-documentation/api-reference.html#webpage",
"url": "writerside-documentation/api-reference.html",
"name": "API Reference | Copenhagen Truck Wash API",
"description": "",
"image": "",
"inLanguage":"en-US"
}</script><!-- End Schema.org --><!-- Schema.org WebSite --><script type="application/ld+json">{
"@type": "WebSite",
"@id": "writerside-documentation/#website",
"url": "writerside-documentation/",
"name": "Copenhagen Truck Wash API Help"
}</script><!-- End Schema.org --></head><body data-id="API-Reference" data-main-title="API Reference" data-article-props="{&quot;seeAlsoStyle&quot;:&quot;links&quot;}" data-template="article" data-breadcrumbs=""><div class="wrapper"><main class="panel _main"><header class="panel__header"><div class="container"><h3>Copenhagen Truck Wash API Help</h3><div class="panel-trigger"></div></div></header><section class="panel__content"><div class="container"><article class="article" data-shortcut-switcher="inactive"><h1 data-toc="API-Reference" id="API-Reference.topic">API Reference</h1><p id="z1opr22_2">Comprehensive API reference generated from the repository root <code class="code" id="z1opr22_3">openapi.yaml</code>.</p><div class="last-modified">17 March 2026</div><div data-feedback-placeholder="true"></div><div class="navigation-links _bottom"><a href="error-handling.html" class="navigation-links__prev">Error Handling</a><a href="tag-authentication.html" class="navigation-links__next">Authentication</a></div></article><div id="disqus_thread"></div></div></section></main></div><script src="https://resources.jetbrains.com/writerside/apidoc/6.26.0-b913/app.js"></script></body></html>
@@ -0,0 +1,20 @@
<!DOCTYPE html SYSTEM "about:legacy-compat">
<html lang="en-US" data-preset="contrast" data-primary-color="#307FFF"><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8"><meta charset="UTF-8"><meta name="robots" content="noindex"><meta name="built-on" content="2026-03-17T15:38:10.245691405"><title>Authentication | Copenhagen Truck Wash API</title><script type="application/json" id="virtual-toc-data">[{"id":"api-token","level":0,"title":"Bearer JWT Header","anchor":"#api-token"},{"id":"subuser-customer-targeting","level":0,"title":"Subuser Customer Targeting","anchor":"#subuser-customer-targeting"}]</script><script type="application/json" id="topic-shortcuts"></script><link href="https://resources.jetbrains.com/writerside/apidoc/6.26.0-b913/app.css" rel="stylesheet"><meta name="msapplication-TileColor" content="#000000"><link rel="apple-touch-icon" sizes="180x180" href="https://jetbrains.com/apple-touch-icon.png"><link rel="icon" type="image/png" sizes="32x32" href="https://jetbrains.com/favicon-32x32.png"><link rel="icon" type="image/png" sizes="16x16" href="https://jetbrains.com/favicon-16x16.png"><meta name="msapplication-TileImage" content="https://resources.jetbrains.com/storage/ui/favicons/mstile-144x144.png"><meta name="msapplication-square70x70logo" content="https://resources.jetbrains.com/storage/ui/favicons/mstile-70x70.png"><meta name="msapplication-square150x150logo" content="https://resources.jetbrains.com/storage/ui/favicons/mstile-150x150.png"><meta name="msapplication-wide310x150logo" content="https://resources.jetbrains.com/storage/ui/favicons/mstile-310x150.png"><meta name="msapplication-square310x310logo" content="https://resources.jetbrains.com/storage/ui/favicons/mstile-310x310.png"><meta name="image" content=""><!-- Open Graph --><meta property="og:title" content="Authentication | Copenhagen Truck Wash API"><meta property="og:description" content=""><meta property="og:image" content=""><meta property="og:site_name" content="Copenhagen Truck Wash API Help"><meta property="og:type" content="website"><meta property="og:locale" content="en_US"><meta property="og:url" content="writerside-documentation/authentication.html"><!-- End Open Graph --><!-- Twitter Card --><meta name="twitter:card" content="summary_large_image"><meta name="twitter:site" content=""><meta name="twitter:title" content="Authentication | Copenhagen Truck Wash API"><meta name="twitter:description" content=""><meta name="twitter:creator" content=""><meta name="twitter:image:src" content=""><!-- End Twitter Card --><!-- Schema.org WebPage --><script type="application/ld+json">{
"@context": "http://schema.org",
"@type": "WebPage",
"@id": "writerside-documentation/authentication.html#webpage",
"url": "writerside-documentation/authentication.html",
"name": "Authentication | Copenhagen Truck Wash API",
"description": "",
"image": "",
"inLanguage":"en-US"
}</script><!-- End Schema.org --><!-- Schema.org WebSite --><script type="application/ld+json">{
"@type": "WebSite",
"@id": "writerside-documentation/#website",
"url": "writerside-documentation/",
"name": "Copenhagen Truck Wash API Help"
}</script><!-- End Schema.org --></head><body data-id="Authentication" data-main-title="Authentication" data-article-props="{&quot;seeAlsoStyle&quot;:&quot;links&quot;}" data-template="article" data-breadcrumbs="API-Overview.topic|API Overview"><div class="wrapper"><main class="panel _main"><header class="panel__header"><div class="container"><h3>Copenhagen Truck Wash API Help</h3><div class="panel-trigger"></div></div></header><section class="panel__content"><div class="container"><article class="article" data-shortcut-switcher="inactive"><h1 data-toc="Authentication" id="Authentication.topic">Authentication</h1><p id="kim5nc_2">The API uses the <code class="code" id="kim5nc_5">BearerAuth</code> security scheme (HTTP Bearer, JWT).</p><section class="chapter"><h2 id="api-token" data-toc="api-token">Bearer JWT Header</h2><p id="kim5nc_6">Get a token from <code class="code" id="kim5nc_8">/auth/login</code> or <code class="code" id="kim5nc_9">/auth/employee/login</code>, then send:</p><div class="code-block" data-lang="http">
Authorization: Bearer YOUR_API_TOKEN
</div></section><section class="chapter"><h2 id="subuser-customer-targeting" data-toc="subuser-customer-targeting">Subuser Customer Targeting</h2><p id="kim5nc_10">When authenticated as a subuser, include a target customer header for customer-scoped endpoints:</p><div class="code-block" data-lang="http">
X-Customer-Number: 123456
</div></section><div class="last-modified">17 March 2026</div><div data-feedback-placeholder="true"></div><div class="navigation-links _bottom"><a href="api-overview.html" class="navigation-links__prev">API Overview</a><a href="error-handling.html" class="navigation-links__next">Error Handling</a></div></article><div id="disqus_thread"></div></div></section></main></div><script src="https://resources.jetbrains.com/writerside/apidoc/6.26.0-b913/app.js"></script></body></html>
@@ -0,0 +1,26 @@
<!DOCTYPE html SYSTEM "about:legacy-compat">
<html lang="en-US" data-preset="contrast" data-primary-color="#307FFF"><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8"><meta charset="UTF-8"><meta name="robots" content="noindex"><meta name="built-on" content="2026-03-17T15:44:02.492592831"><title>Create/place a flash call via Bird | Copenhagen Truck Wash API</title><script type="application/json" id="virtual-toc-data">[{"id":"endpoint","level":0,"title":"Endpoint","anchor":"#endpoint"},{"id":"operation","level":0,"title":"Operation","anchor":"#operation"},{"id":"authentication","level":0,"title":"Authentication","anchor":"#authentication"},{"id":"parameters","level":0,"title":"Parameters","anchor":"#parameters"},{"id":"request-body","level":0,"title":"Request Body","anchor":"#request-body"},{"id":"responses","level":0,"title":"Responses","anchor":"#responses"}]</script><script type="application/json" id="topic-shortcuts"></script><link href="https://resources.jetbrains.com/writerside/apidoc/6.26.0-b913/app.css" rel="stylesheet"><meta name="msapplication-TileColor" content="#000000"><link rel="apple-touch-icon" sizes="180x180" href="https://jetbrains.com/apple-touch-icon.png"><link rel="icon" type="image/png" sizes="32x32" href="https://jetbrains.com/favicon-32x32.png"><link rel="icon" type="image/png" sizes="16x16" href="https://jetbrains.com/favicon-16x16.png"><meta name="msapplication-TileImage" content="https://resources.jetbrains.com/storage/ui/favicons/mstile-144x144.png"><meta name="msapplication-square70x70logo" content="https://resources.jetbrains.com/storage/ui/favicons/mstile-70x70.png"><meta name="msapplication-square150x150logo" content="https://resources.jetbrains.com/storage/ui/favicons/mstile-150x150.png"><meta name="msapplication-wide310x150logo" content="https://resources.jetbrains.com/storage/ui/favicons/mstile-310x150.png"><meta name="msapplication-square310x310logo" content="https://resources.jetbrains.com/storage/ui/favicons/mstile-310x310.png"><meta name="image" content=""><!-- Open Graph --><meta property="og:title" content="Create/place a flash call via Bird | Copenhagen Truck Wash API"><meta property="og:description" content=""><meta property="og:image" content=""><meta property="og:site_name" content="Copenhagen Truck Wash API Help"><meta property="og:type" content="website"><meta property="og:locale" content="en_US"><meta property="og:url" content="writerside-documentation/birdcreateflashcall.html"><!-- End Open Graph --><!-- Twitter Card --><meta name="twitter:card" content="summary_large_image"><meta name="twitter:site" content=""><meta name="twitter:title" content="Create/place a flash call via Bird | Copenhagen Truck Wash API"><meta name="twitter:description" content=""><meta name="twitter:creator" content=""><meta name="twitter:image:src" content=""><!-- End Twitter Card --><!-- Schema.org WebPage --><script type="application/ld+json">{
"@context": "http://schema.org",
"@type": "WebPage",
"@id": "writerside-documentation/birdcreateflashcall.html#webpage",
"url": "writerside-documentation/birdcreateflashcall.html",
"name": "Create/place a flash call via Bird | Copenhagen Truck Wash API",
"description": "",
"image": "",
"inLanguage":"en-US"
}</script><!-- End Schema.org --><!-- Schema.org WebSite --><script type="application/ld+json">{
"@type": "WebSite",
"@id": "writerside-documentation/#website",
"url": "writerside-documentation/",
"name": "Copenhagen Truck Wash API Help"
}</script><!-- End Schema.org --></head><body data-id="birdCreateFlashCall" data-main-title="Create/place a flash call via Bird" data-article-props="{&quot;seeAlsoStyle&quot;:&quot;links&quot;}" data-template="article" data-breadcrumbs="API-Reference.topic|API Reference///Tag_Bird.topic|Bird///Tag_Bird_Page_1.topic|Bird - Page 1 of 1"><div class="wrapper"><main class="panel _main"><header class="panel__header"><div class="container"><h3>Copenhagen Truck Wash API Help</h3><div class="panel-trigger"></div></div></header><section class="panel__content"><div class="container"><article class="article" data-shortcut-switcher="inactive"><h1 data-toc="birdCreateFlashCall" id="birdCreateFlashCall.topic">Create/place a flash call via Bird</h1><p id="-pc6vet_2">This endpoint documentation is generated directly from <code class="code" id="-pc6vet_9">openapi.yaml</code>.</p><section class="chapter"><h2 id="endpoint" data-toc="endpoint">Endpoint</h2><div class="code-block" data-lang="http">POST /bird/voice/flash-calls</div></section><section class="chapter"><h2 id="operation" data-toc="operation">Operation</h2><p id="-pc6vet_11">Operation ID: <code class="code" id="-pc6vet_13">birdCreateFlashCall</code></p><p id="-pc6vet_12">Create/place a flash call via Bird</p></section><section class="chapter"><h2 id="authentication" data-toc="authentication">Authentication</h2><p id="-pc6vet_14">Security requirements:</p><div class="table-wrapper"><table class="wide" id="-pc6vet_15"><thead><tr class="ijRowHead" id="-pc6vet_16"><th id="-pc6vet_18"><p>Scheme</p></th><th id="-pc6vet_19"><p>Scopes</p></th></tr></thead><tbody><tr id="-pc6vet_17"><td id="-pc6vet_20"><p>BearerAuth</p></td><td id="-pc6vet_21"><p>-</p></td></tr></tbody></table></div></section><section class="chapter"><h2 id="parameters" data-toc="parameters">Parameters</h2><div class="table-wrapper"><table class="wide" id="-pc6vet_22"><thead><tr class="ijRowHead" id="-pc6vet_23"><th id="-pc6vet_26"><p>Name</p></th><th id="-pc6vet_27"><p>In</p></th><th id="-pc6vet_28"><p>Required</p></th><th id="-pc6vet_29"><p>Type</p></th><th id="-pc6vet_30"><p>Description</p></th></tr></thead><tbody><tr id="-pc6vet_24"><td id="-pc6vet_31"><p>workspaceId</p></td><td id="-pc6vet_32"><p>query</p></td><td id="-pc6vet_33"><p>no</p></td><td id="-pc6vet_34"><p>string</p></td><td id="-pc6vet_35"><p>Bird Workspace identifier (falls back to module configuration if omitted)</p></td></tr><tr id="-pc6vet_25"><td id="-pc6vet_36"><p>channelId</p></td><td id="-pc6vet_37"><p>query</p></td><td id="-pc6vet_38"><p>no</p></td><td id="-pc6vet_39"><p>string</p></td><td id="-pc6vet_40"><p>Bird Channel identifier (falls back to module configuration if omitted)</p></td></tr></tbody></table></div></section><section class="chapter"><h2 id="request-body" data-toc="request-body">Request Body</h2><p id="-pc6vet_41">Required: yes.</p><p id="-pc6vet_42">Content type: <code class="code" id="-pc6vet_44">application/json</code></p><div class="code-block" data-lang="json">
{
&quot;additionalProperties&quot;: true,
&quot;type&quot;: &quot;object&quot;
}
</div></section><section class="chapter"><h2 id="responses" data-toc="responses">Responses</h2><div class="table-wrapper"><table class="wide" id="-pc6vet_45"><thead><tr class="ijRowHead" id="-pc6vet_48"><th id="-pc6vet_50"><p>Status</p></th><th id="-pc6vet_51"><p>Description</p></th><th id="-pc6vet_52"><p>Content Types</p></th></tr></thead><tbody><tr id="-pc6vet_49"><td id="-pc6vet_53"><p>200</p></td><td id="-pc6vet_54"><p>Flash call created</p></td><td id="-pc6vet_55"><p>application/json</p></td></tr></tbody></table></div><p id="-pc6vet_46">Schema for response <code class="code" id="-pc6vet_56">200</code> (<code class="code" id="-pc6vet_57">application/json</code>):</p><div class="code-block" data-lang="json">
{
&quot;additionalProperties&quot;: true,
&quot;type&quot;: &quot;object&quot;
}
</div></section><div class="last-modified">17 March 2026</div><div data-feedback-placeholder="true"></div><div class="navigation-links _bottom"><a href="birdlistflashcalls.html" class="navigation-links__prev">List flash calls</a><a href="birdendflashcallbynumbers.html" class="navigation-links__next">Complete/end a flash call using from/to numbers</a></div></article><div id="disqus_thread"></div></div></section></main></div><script src="https://resources.jetbrains.com/writerside/apidoc/6.26.0-b913/app.js"></script></body></html>
@@ -0,0 +1,37 @@
<!DOCTYPE html SYSTEM "about:legacy-compat">
<html lang="en-US" data-preset="contrast" data-primary-color="#307FFF"><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8"><meta charset="UTF-8"><meta name="robots" content="noindex"><meta name="built-on" content="2026-03-17T15:42:09.58052399"><title>Create/place a voice call via Bird | Copenhagen Truck Wash API</title><script type="application/json" id="virtual-toc-data">[{"id":"endpoint","level":0,"title":"Endpoint","anchor":"#endpoint"},{"id":"operation","level":0,"title":"Operation","anchor":"#operation"},{"id":"authentication","level":0,"title":"Authentication","anchor":"#authentication"},{"id":"parameters","level":0,"title":"Parameters","anchor":"#parameters"},{"id":"request-body","level":0,"title":"Request Body","anchor":"#request-body"},{"id":"responses","level":0,"title":"Responses","anchor":"#responses"}]</script><script type="application/json" id="topic-shortcuts"></script><link href="https://resources.jetbrains.com/writerside/apidoc/6.26.0-b913/app.css" rel="stylesheet"><meta name="msapplication-TileColor" content="#000000"><link rel="apple-touch-icon" sizes="180x180" href="https://jetbrains.com/apple-touch-icon.png"><link rel="icon" type="image/png" sizes="32x32" href="https://jetbrains.com/favicon-32x32.png"><link rel="icon" type="image/png" sizes="16x16" href="https://jetbrains.com/favicon-16x16.png"><meta name="msapplication-TileImage" content="https://resources.jetbrains.com/storage/ui/favicons/mstile-144x144.png"><meta name="msapplication-square70x70logo" content="https://resources.jetbrains.com/storage/ui/favicons/mstile-70x70.png"><meta name="msapplication-square150x150logo" content="https://resources.jetbrains.com/storage/ui/favicons/mstile-150x150.png"><meta name="msapplication-wide310x150logo" content="https://resources.jetbrains.com/storage/ui/favicons/mstile-310x150.png"><meta name="msapplication-square310x310logo" content="https://resources.jetbrains.com/storage/ui/favicons/mstile-310x310.png"><meta name="image" content=""><!-- Open Graph --><meta property="og:title" content="Create/place a voice call via Bird | Copenhagen Truck Wash API"><meta property="og:description" content=""><meta property="og:image" content=""><meta property="og:site_name" content="Copenhagen Truck Wash API Help"><meta property="og:type" content="website"><meta property="og:locale" content="en_US"><meta property="og:url" content="writerside-documentation/birdcreatevoicecall.html"><!-- End Open Graph --><!-- Twitter Card --><meta name="twitter:card" content="summary_large_image"><meta name="twitter:site" content=""><meta name="twitter:title" content="Create/place a voice call via Bird | Copenhagen Truck Wash API"><meta name="twitter:description" content=""><meta name="twitter:creator" content=""><meta name="twitter:image:src" content=""><!-- End Twitter Card --><!-- Schema.org WebPage --><script type="application/ld+json">{
"@context": "http://schema.org",
"@type": "WebPage",
"@id": "writerside-documentation/birdcreatevoicecall.html#webpage",
"url": "writerside-documentation/birdcreatevoicecall.html",
"name": "Create/place a voice call via Bird | Copenhagen Truck Wash API",
"description": "",
"image": "",
"inLanguage":"en-US"
}</script><!-- End Schema.org --><!-- Schema.org WebSite --><script type="application/ld+json">{
"@type": "WebSite",
"@id": "writerside-documentation/#website",
"url": "writerside-documentation/",
"name": "Copenhagen Truck Wash API Help"
}</script><!-- End Schema.org --></head><body data-id="birdCreateVoiceCall" data-main-title="Create/place a voice call via Bird" data-article-props="{&quot;seeAlsoStyle&quot;:&quot;links&quot;}" data-template="article" data-breadcrumbs="API-Reference.topic|API Reference///Tag_Bird.topic|Bird///Tag_Bird_Page_1.topic|Bird - Page 1 of 1"><div class="wrapper"><main class="panel _main"><header class="panel__header"><div class="container"><h3>Copenhagen Truck Wash API Help</h3><div class="panel-trigger"></div></div></header><section class="panel__content"><div class="container"><article class="article" data-shortcut-switcher="inactive"><h1 data-toc="birdCreateVoiceCall" id="birdCreateVoiceCall.topic">Create/place a voice call via Bird</h1><p id="z9imszh_2">This endpoint documentation is generated directly from <code class="code" id="z9imszh_9">openapi.yaml</code>.</p><section class="chapter"><h2 id="endpoint" data-toc="endpoint">Endpoint</h2><div class="code-block" data-lang="http">POST /bird/voice/calls</div></section><section class="chapter"><h2 id="operation" data-toc="operation">Operation</h2><p id="z9imszh_11">Operation ID: <code class="code" id="z9imszh_13">birdCreateVoiceCall</code></p><p id="z9imszh_12">Create/place a voice call via Bird</p></section><section class="chapter"><h2 id="authentication" data-toc="authentication">Authentication</h2><p id="z9imszh_14">Security requirements:</p><div class="table-wrapper"><table class="wide" id="z9imszh_15"><thead><tr class="ijRowHead" id="z9imszh_16"><th id="z9imszh_18"><p>Scheme</p></th><th id="z9imszh_19"><p>Scopes</p></th></tr></thead><tbody><tr id="z9imszh_17"><td id="z9imszh_20"><p>BearerAuth</p></td><td id="z9imszh_21"><p>-</p></td></tr></tbody></table></div></section><section class="chapter"><h2 id="parameters" data-toc="parameters">Parameters</h2><div class="table-wrapper"><table class="wide" id="z9imszh_22"><thead><tr class="ijRowHead" id="z9imszh_23"><th id="z9imszh_26"><p>Name</p></th><th id="z9imszh_27"><p>In</p></th><th id="z9imszh_28"><p>Required</p></th><th id="z9imszh_29"><p>Type</p></th><th id="z9imszh_30"><p>Description</p></th></tr></thead><tbody><tr id="z9imszh_24"><td id="z9imszh_31"><p>workspaceId</p></td><td id="z9imszh_32"><p>query</p></td><td id="z9imszh_33"><p>no</p></td><td id="z9imszh_34"><p>string</p></td><td id="z9imszh_35"><p>Bird Workspace identifier (falls back to module configuration if omitted)</p></td></tr><tr id="z9imszh_25"><td id="z9imszh_36"><p>channelId</p></td><td id="z9imszh_37"><p>query</p></td><td id="z9imszh_38"><p>no</p></td><td id="z9imszh_39"><p>string</p></td><td id="z9imszh_40"><p>Bird Channel identifier (falls back to module configuration if omitted)</p></td></tr></tbody></table></div></section><section class="chapter"><h2 id="request-body" data-toc="request-body">Request Body</h2><p id="z9imszh_41">Required: yes.</p><p id="z9imszh_42">Content type: <code class="code" id="z9imszh_44">application/json</code></p><div class="code-block" data-lang="json">
{
&quot;additionalProperties&quot;: true,
&quot;properties&quot;: {
&quot;from&quot;: {
&quot;description&quot;: &quot;E.164 phone number of the caller (sender)&quot;,
&quot;example&quot;: &quot;+4599988877&quot;,
&quot;type&quot;: &quot;string&quot;
},
&quot;to&quot;: {
&quot;description&quot;: &quot;E.164 phone number of the callee&quot;,
&quot;example&quot;: &quot;+4511122233&quot;,
&quot;type&quot;: &quot;string&quot;
}
},
&quot;type&quot;: &quot;object&quot;
}
</div></section><section class="chapter"><h2 id="responses" data-toc="responses">Responses</h2><div class="table-wrapper"><table class="wide" id="z9imszh_45"><thead><tr class="ijRowHead" id="z9imszh_48"><th id="z9imszh_50"><p>Status</p></th><th id="z9imszh_51"><p>Description</p></th><th id="z9imszh_52"><p>Content Types</p></th></tr></thead><tbody><tr id="z9imszh_49"><td id="z9imszh_53"><p>200</p></td><td id="z9imszh_54"><p>Call created</p></td><td id="z9imszh_55"><p>application/json</p></td></tr></tbody></table></div><p id="z9imszh_46">Schema for response <code class="code" id="z9imszh_56">200</code> (<code class="code" id="z9imszh_57">application/json</code>):</p><div class="code-block" data-lang="json">
{
&quot;$ref&quot;: &quot;#/components/schemas/BirdVoiceCallSingleResponse&quot;
}
</div></section><div class="last-modified">17 March 2026</div><div data-feedback-placeholder="true"></div><div class="navigation-links _bottom"><a href="birdlistvoicecalls.html" class="navigation-links__prev">List voice calls</a><a href="birdtestoutboundvoicecall.html" class="navigation-links__next">Place a test outbound call and hang up when accepted</a></div></article><div id="disqus_thread"></div></div></section></main></div><script src="https://resources.jetbrains.com/writerside/apidoc/6.26.0-b913/app.js"></script></body></html>
@@ -0,0 +1,21 @@
<!DOCTYPE html SYSTEM "about:legacy-compat">
<html lang="en-US" data-preset="contrast" data-primary-color="#307FFF"><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8"><meta charset="UTF-8"><meta name="robots" content="noindex"><meta name="built-on" content="2026-03-17T15:41:32.205987443"><title>Delete/release a number by ID | Copenhagen Truck Wash API</title><script type="application/json" id="virtual-toc-data">[{"id":"endpoint","level":0,"title":"Endpoint","anchor":"#endpoint"},{"id":"operation","level":0,"title":"Operation","anchor":"#operation"},{"id":"authentication","level":0,"title":"Authentication","anchor":"#authentication"},{"id":"parameters","level":0,"title":"Parameters","anchor":"#parameters"},{"id":"responses","level":0,"title":"Responses","anchor":"#responses"}]</script><script type="application/json" id="topic-shortcuts"></script><link href="https://resources.jetbrains.com/writerside/apidoc/6.26.0-b913/app.css" rel="stylesheet"><meta name="msapplication-TileColor" content="#000000"><link rel="apple-touch-icon" sizes="180x180" href="https://jetbrains.com/apple-touch-icon.png"><link rel="icon" type="image/png" sizes="32x32" href="https://jetbrains.com/favicon-32x32.png"><link rel="icon" type="image/png" sizes="16x16" href="https://jetbrains.com/favicon-16x16.png"><meta name="msapplication-TileImage" content="https://resources.jetbrains.com/storage/ui/favicons/mstile-144x144.png"><meta name="msapplication-square70x70logo" content="https://resources.jetbrains.com/storage/ui/favicons/mstile-70x70.png"><meta name="msapplication-square150x150logo" content="https://resources.jetbrains.com/storage/ui/favicons/mstile-150x150.png"><meta name="msapplication-wide310x150logo" content="https://resources.jetbrains.com/storage/ui/favicons/mstile-310x150.png"><meta name="msapplication-square310x310logo" content="https://resources.jetbrains.com/storage/ui/favicons/mstile-310x310.png"><meta name="image" content=""><!-- Open Graph --><meta property="og:title" content="Delete/release a number by ID | Copenhagen Truck Wash API"><meta property="og:description" content=""><meta property="og:image" content=""><meta property="og:site_name" content="Copenhagen Truck Wash API Help"><meta property="og:type" content="website"><meta property="og:locale" content="en_US"><meta property="og:url" content="writerside-documentation/birddeletenumber.html"><!-- End Open Graph --><!-- Twitter Card --><meta name="twitter:card" content="summary_large_image"><meta name="twitter:site" content=""><meta name="twitter:title" content="Delete/release a number by ID | Copenhagen Truck Wash API"><meta name="twitter:description" content=""><meta name="twitter:creator" content=""><meta name="twitter:image:src" content=""><!-- End Twitter Card --><!-- Schema.org WebPage --><script type="application/ld+json">{
"@context": "http://schema.org",
"@type": "WebPage",
"@id": "writerside-documentation/birddeletenumber.html#webpage",
"url": "writerside-documentation/birddeletenumber.html",
"name": "Delete/release a number by ID | Copenhagen Truck Wash API",
"description": "",
"image": "",
"inLanguage":"en-US"
}</script><!-- End Schema.org --><!-- Schema.org WebSite --><script type="application/ld+json">{
"@type": "WebSite",
"@id": "writerside-documentation/#website",
"url": "writerside-documentation/",
"name": "Copenhagen Truck Wash API Help"
}</script><!-- End Schema.org --></head><body data-id="birdDeleteNumber" data-main-title="Delete/release a number by ID" data-article-props="{&quot;seeAlsoStyle&quot;:&quot;links&quot;}" data-template="article" data-breadcrumbs="API-Reference.topic|API Reference///Tag_Bird.topic|Bird///Tag_Bird_Page_1.topic|Bird - Page 1 of 1"><div class="wrapper"><main class="panel _main"><header class="panel__header"><div class="container"><h3>Copenhagen Truck Wash API Help</h3><div class="panel-trigger"></div></div></header><section class="panel__content"><div class="container"><article class="article" data-shortcut-switcher="inactive"><h1 data-toc="birdDeleteNumber" id="birdDeleteNumber.topic">Delete/release a number by ID</h1><p id="-a2hln7_2">This endpoint documentation is generated directly from <code class="code" id="-a2hln7_8">openapi.yaml</code>.</p><section class="chapter"><h2 id="endpoint" data-toc="endpoint">Endpoint</h2><div class="code-block" data-lang="http">DELETE /bird/numbers/{id}</div></section><section class="chapter"><h2 id="operation" data-toc="operation">Operation</h2><p id="-a2hln7_10">Operation ID: <code class="code" id="-a2hln7_12">birdDeleteNumber</code></p><p id="-a2hln7_11">Delete/release a number by ID</p></section><section class="chapter"><h2 id="authentication" data-toc="authentication">Authentication</h2><p id="-a2hln7_13">Security requirements:</p><div class="table-wrapper"><table class="wide" id="-a2hln7_14"><thead><tr class="ijRowHead" id="-a2hln7_15"><th id="-a2hln7_17"><p>Scheme</p></th><th id="-a2hln7_18"><p>Scopes</p></th></tr></thead><tbody><tr id="-a2hln7_16"><td id="-a2hln7_19"><p>BearerAuth</p></td><td id="-a2hln7_20"><p>-</p></td></tr></tbody></table></div></section><section class="chapter"><h2 id="parameters" data-toc="parameters">Parameters</h2><div class="table-wrapper"><table class="wide" id="-a2hln7_21"><thead><tr class="ijRowHead" id="-a2hln7_22"><th id="-a2hln7_25"><p>Name</p></th><th id="-a2hln7_26"><p>In</p></th><th id="-a2hln7_27"><p>Required</p></th><th id="-a2hln7_28"><p>Type</p></th><th id="-a2hln7_29"><p>Description</p></th></tr></thead><tbody><tr id="-a2hln7_23"><td id="-a2hln7_30"><p>workspaceId</p></td><td id="-a2hln7_31"><p>query</p></td><td id="-a2hln7_32"><p>no</p></td><td id="-a2hln7_33"><p>string</p></td><td id="-a2hln7_34"><p>Bird Workspace identifier (optional if configured)</p></td></tr><tr id="-a2hln7_24"><td id="-a2hln7_35"><p>id</p></td><td id="-a2hln7_36"><p>path</p></td><td id="-a2hln7_37"><p>yes</p></td><td id="-a2hln7_38"><p>string</p></td><td id="-a2hln7_39"></td></tr></tbody></table></div></section><section class="chapter"><h2 id="responses" data-toc="responses">Responses</h2><div class="table-wrapper"><table class="wide" id="-a2hln7_40"><thead><tr class="ijRowHead" id="-a2hln7_43"><th id="-a2hln7_45"><p>Status</p></th><th id="-a2hln7_46"><p>Description</p></th><th id="-a2hln7_47"><p>Content Types</p></th></tr></thead><tbody><tr id="-a2hln7_44"><td id="-a2hln7_48"><p>200</p></td><td id="-a2hln7_49"><p>Number deletion/release accepted</p></td><td id="-a2hln7_50"><p>application/json</p></td></tr></tbody></table></div><p id="-a2hln7_41">Schema for response <code class="code" id="-a2hln7_51">200</code> (<code class="code" id="-a2hln7_52">application/json</code>):</p><div class="code-block" data-lang="json">
{
&quot;additionalProperties&quot;: true,
&quot;type&quot;: &quot;object&quot;
}
</div></section><div class="last-modified">17 March 2026</div><div data-feedback-placeholder="true"></div><div class="navigation-links _bottom"><a href="birdlistnumbers.html" class="navigation-links__prev">List your numbers</a><a href="birdgetnumber.html" class="navigation-links__next">Get a number by ID</a></div></article><div id="disqus_thread"></div></div></section></main></div><script src="https://resources.jetbrains.com/writerside/apidoc/6.26.0-b913/app.js"></script></body></html>
@@ -0,0 +1,26 @@
<!DOCTYPE html SYSTEM "about:legacy-compat">
<html lang="en-US" data-preset="contrast" data-primary-color="#307FFF"><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8"><meta charset="UTF-8"><meta name="robots" content="noindex"><meta name="built-on" content="2026-03-17T15:45:16.203832432"><title>Complete/end a flash call by ID | Copenhagen Truck Wash API</title><script type="application/json" id="virtual-toc-data">[{"id":"endpoint","level":0,"title":"Endpoint","anchor":"#endpoint"},{"id":"operation","level":0,"title":"Operation","anchor":"#operation"},{"id":"authentication","level":0,"title":"Authentication","anchor":"#authentication"},{"id":"parameters","level":0,"title":"Parameters","anchor":"#parameters"},{"id":"request-body","level":0,"title":"Request Body","anchor":"#request-body"},{"id":"responses","level":0,"title":"Responses","anchor":"#responses"}]</script><script type="application/json" id="topic-shortcuts"></script><link href="https://resources.jetbrains.com/writerside/apidoc/6.26.0-b913/app.css" rel="stylesheet"><meta name="msapplication-TileColor" content="#000000"><link rel="apple-touch-icon" sizes="180x180" href="https://jetbrains.com/apple-touch-icon.png"><link rel="icon" type="image/png" sizes="32x32" href="https://jetbrains.com/favicon-32x32.png"><link rel="icon" type="image/png" sizes="16x16" href="https://jetbrains.com/favicon-16x16.png"><meta name="msapplication-TileImage" content="https://resources.jetbrains.com/storage/ui/favicons/mstile-144x144.png"><meta name="msapplication-square70x70logo" content="https://resources.jetbrains.com/storage/ui/favicons/mstile-70x70.png"><meta name="msapplication-square150x150logo" content="https://resources.jetbrains.com/storage/ui/favicons/mstile-150x150.png"><meta name="msapplication-wide310x150logo" content="https://resources.jetbrains.com/storage/ui/favicons/mstile-310x150.png"><meta name="msapplication-square310x310logo" content="https://resources.jetbrains.com/storage/ui/favicons/mstile-310x310.png"><meta name="image" content=""><!-- Open Graph --><meta property="og:title" content="Complete/end a flash call by ID | Copenhagen Truck Wash API"><meta property="og:description" content=""><meta property="og:image" content=""><meta property="og:site_name" content="Copenhagen Truck Wash API Help"><meta property="og:type" content="website"><meta property="og:locale" content="en_US"><meta property="og:url" content="writerside-documentation/birdendflashcall.html"><!-- End Open Graph --><!-- Twitter Card --><meta name="twitter:card" content="summary_large_image"><meta name="twitter:site" content=""><meta name="twitter:title" content="Complete/end a flash call by ID | Copenhagen Truck Wash API"><meta name="twitter:description" content=""><meta name="twitter:creator" content=""><meta name="twitter:image:src" content=""><!-- End Twitter Card --><!-- Schema.org WebPage --><script type="application/ld+json">{
"@context": "http://schema.org",
"@type": "WebPage",
"@id": "writerside-documentation/birdendflashcall.html#webpage",
"url": "writerside-documentation/birdendflashcall.html",
"name": "Complete/end a flash call by ID | Copenhagen Truck Wash API",
"description": "",
"image": "",
"inLanguage":"en-US"
}</script><!-- End Schema.org --><!-- Schema.org WebSite --><script type="application/ld+json">{
"@type": "WebSite",
"@id": "writerside-documentation/#website",
"url": "writerside-documentation/",
"name": "Copenhagen Truck Wash API Help"
}</script><!-- End Schema.org --></head><body data-id="birdEndFlashCall" data-main-title="Complete/end a flash call by ID" data-article-props="{&quot;seeAlsoStyle&quot;:&quot;links&quot;}" data-template="article" data-breadcrumbs="API-Reference.topic|API Reference///Tag_Bird.topic|Bird///Tag_Bird_Page_1.topic|Bird - Page 1 of 1"><div class="wrapper"><main class="panel _main"><header class="panel__header"><div class="container"><h3>Copenhagen Truck Wash API Help</h3><div class="panel-trigger"></div></div></header><section class="panel__content"><div class="container"><article class="article" data-shortcut-switcher="inactive"><h1 data-toc="birdEndFlashCall" id="birdEndFlashCall.topic">Complete/end a flash call by ID</h1><p id="h3tr70_2">This endpoint documentation is generated directly from <code class="code" id="h3tr70_9">openapi.yaml</code>.</p><section class="chapter"><h2 id="endpoint" data-toc="endpoint">Endpoint</h2><div class="code-block" data-lang="http">POST /bird/voice/flash-calls/{id}</div></section><section class="chapter"><h2 id="operation" data-toc="operation">Operation</h2><p id="h3tr70_11">Operation ID: <code class="code" id="h3tr70_13">birdEndFlashCall</code></p><p id="h3tr70_12">Posts a completion/update payload to the flash call resource to finalize verification.</p></section><section class="chapter"><h2 id="authentication" data-toc="authentication">Authentication</h2><p id="h3tr70_14">Security requirements:</p><div class="table-wrapper"><table class="wide" id="h3tr70_15"><thead><tr class="ijRowHead" id="h3tr70_16"><th id="h3tr70_18"><p>Scheme</p></th><th id="h3tr70_19"><p>Scopes</p></th></tr></thead><tbody><tr id="h3tr70_17"><td id="h3tr70_20"><p>BearerAuth</p></td><td id="h3tr70_21"><p>-</p></td></tr></tbody></table></div></section><section class="chapter"><h2 id="parameters" data-toc="parameters">Parameters</h2><div class="table-wrapper"><table class="wide" id="h3tr70_22"><thead><tr class="ijRowHead" id="h3tr70_23"><th id="h3tr70_27"><p>Name</p></th><th id="h3tr70_28"><p>In</p></th><th id="h3tr70_29"><p>Required</p></th><th id="h3tr70_30"><p>Type</p></th><th id="h3tr70_31"><p>Description</p></th></tr></thead><tbody><tr id="h3tr70_24"><td id="h3tr70_32"><p>workspaceId</p></td><td id="h3tr70_33"><p>query</p></td><td id="h3tr70_34"><p>no</p></td><td id="h3tr70_35"><p>string</p></td><td id="h3tr70_36"><p>Bird Workspace identifier (falls back to module configuration if omitted)</p></td></tr><tr id="h3tr70_25"><td id="h3tr70_37"><p>channelId</p></td><td id="h3tr70_38"><p>query</p></td><td id="h3tr70_39"><p>no</p></td><td id="h3tr70_40"><p>string</p></td><td id="h3tr70_41"><p>Bird Channel identifier (falls back to module configuration if omitted)</p></td></tr><tr id="h3tr70_26"><td id="h3tr70_42"><p>id</p></td><td id="h3tr70_43"><p>path</p></td><td id="h3tr70_44"><p>yes</p></td><td id="h3tr70_45"><p>string</p></td><td id="h3tr70_46"></td></tr></tbody></table></div></section><section class="chapter"><h2 id="request-body" data-toc="request-body">Request Body</h2><p id="h3tr70_47">Required: no.</p><p id="h3tr70_48">Content type: <code class="code" id="h3tr70_50">application/json</code></p><div class="code-block" data-lang="json">
{
&quot;additionalProperties&quot;: true,
&quot;type&quot;: &quot;object&quot;
}
</div></section><section class="chapter"><h2 id="responses" data-toc="responses">Responses</h2><div class="table-wrapper"><table class="wide" id="h3tr70_51"><thead><tr class="ijRowHead" id="h3tr70_54"><th id="h3tr70_56"><p>Status</p></th><th id="h3tr70_57"><p>Description</p></th><th id="h3tr70_58"><p>Content Types</p></th></tr></thead><tbody><tr id="h3tr70_55"><td id="h3tr70_59"><p>200</p></td><td id="h3tr70_60"><p>Flash call completed</p></td><td id="h3tr70_61"><p>application/json</p></td></tr></tbody></table></div><p id="h3tr70_52">Schema for response <code class="code" id="h3tr70_62">200</code> (<code class="code" id="h3tr70_63">application/json</code>):</p><div class="code-block" data-lang="json">
{
&quot;additionalProperties&quot;: true,
&quot;type&quot;: &quot;object&quot;
}
</div></section><div class="last-modified">17 March 2026</div><div data-feedback-placeholder="true"></div><div class="navigation-links _bottom"><a href="birdgetflashcall.html" class="navigation-links__prev">Get a flash call by ID</a><a href="config-module-gatewayapi.html" class="navigation-links__next">GatewayAPI</a></div></article><div id="disqus_thread"></div></div></section></main></div><script src="https://resources.jetbrains.com/writerside/apidoc/6.26.0-b913/app.js"></script></body></html>
@@ -0,0 +1,38 @@
<!DOCTYPE html SYSTEM "about:legacy-compat">
<html lang="en-US" data-preset="contrast" data-primary-color="#307FFF"><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8"><meta charset="UTF-8"><meta name="robots" content="noindex"><meta name="built-on" content="2026-03-17T15:44:23.260820422"><title>Complete/end a flash call using from/to numbers | Copenhagen Truck Wash API</title><script type="application/json" id="virtual-toc-data">[{"id":"endpoint","level":0,"title":"Endpoint","anchor":"#endpoint"},{"id":"operation","level":0,"title":"Operation","anchor":"#operation"},{"id":"authentication","level":0,"title":"Authentication","anchor":"#authentication"},{"id":"parameters","level":0,"title":"Parameters","anchor":"#parameters"},{"id":"request-body","level":0,"title":"Request Body","anchor":"#request-body"},{"id":"responses","level":0,"title":"Responses","anchor":"#responses"}]</script><script type="application/json" id="topic-shortcuts"></script><link href="https://resources.jetbrains.com/writerside/apidoc/6.26.0-b913/app.css" rel="stylesheet"><meta name="msapplication-TileColor" content="#000000"><link rel="apple-touch-icon" sizes="180x180" href="https://jetbrains.com/apple-touch-icon.png"><link rel="icon" type="image/png" sizes="32x32" href="https://jetbrains.com/favicon-32x32.png"><link rel="icon" type="image/png" sizes="16x16" href="https://jetbrains.com/favicon-16x16.png"><meta name="msapplication-TileImage" content="https://resources.jetbrains.com/storage/ui/favicons/mstile-144x144.png"><meta name="msapplication-square70x70logo" content="https://resources.jetbrains.com/storage/ui/favicons/mstile-70x70.png"><meta name="msapplication-square150x150logo" content="https://resources.jetbrains.com/storage/ui/favicons/mstile-150x150.png"><meta name="msapplication-wide310x150logo" content="https://resources.jetbrains.com/storage/ui/favicons/mstile-310x150.png"><meta name="msapplication-square310x310logo" content="https://resources.jetbrains.com/storage/ui/favicons/mstile-310x310.png"><meta name="image" content=""><!-- Open Graph --><meta property="og:title" content="Complete/end a flash call using from/to numbers | Copenhagen Truck Wash API"><meta property="og:description" content=""><meta property="og:image" content=""><meta property="og:site_name" content="Copenhagen Truck Wash API Help"><meta property="og:type" content="website"><meta property="og:locale" content="en_US"><meta property="og:url" content="writerside-documentation/birdendflashcallbynumbers.html"><!-- End Open Graph --><!-- Twitter Card --><meta name="twitter:card" content="summary_large_image"><meta name="twitter:site" content=""><meta name="twitter:title" content="Complete/end a flash call using from/to numbers | Copenhagen Truck Wash API"><meta name="twitter:description" content=""><meta name="twitter:creator" content=""><meta name="twitter:image:src" content=""><!-- End Twitter Card --><!-- Schema.org WebPage --><script type="application/ld+json">{
"@context": "http://schema.org",
"@type": "WebPage",
"@id": "writerside-documentation/birdendflashcallbynumbers.html#webpage",
"url": "writerside-documentation/birdendflashcallbynumbers.html",
"name": "Complete/end a flash call using from/to numbers | Copenhagen Truck Wash API",
"description": "",
"image": "",
"inLanguage":"en-US"
}</script><!-- End Schema.org --><!-- Schema.org WebSite --><script type="application/ld+json">{
"@type": "WebSite",
"@id": "writerside-documentation/#website",
"url": "writerside-documentation/",
"name": "Copenhagen Truck Wash API Help"
}</script><!-- End Schema.org --></head><body data-id="birdEndFlashCallByNumbers" data-main-title="Complete/end a flash call using from/to numbers" data-article-props="{&quot;seeAlsoStyle&quot;:&quot;links&quot;}" data-template="article" data-breadcrumbs="API-Reference.topic|API Reference///Tag_Bird.topic|Bird///Tag_Bird_Page_1.topic|Bird - Page 1 of 1"><div class="wrapper"><main class="panel _main"><header class="panel__header"><div class="container"><h3>Copenhagen Truck Wash API Help</h3><div class="panel-trigger"></div></div></header><section class="panel__content"><div class="container"><article class="article" data-shortcut-switcher="inactive"><h1 data-toc="birdEndFlashCallByNumbers" id="birdEndFlashCallByNumbers.topic">Complete/end a flash call using from/to numbers</h1><p id="kb1uvd_2">This endpoint documentation is generated directly from <code class="code" id="kb1uvd_9">openapi.yaml</code>.</p><section class="chapter"><h2 id="endpoint" data-toc="endpoint">Endpoint</h2><div class="code-block" data-lang="http">POST /bird/voice/flash-calls/end</div></section><section class="chapter"><h2 id="operation" data-toc="operation">Operation</h2><p id="kb1uvd_11">Operation ID: <code class="code" id="kb1uvd_13">birdEndFlashCallByNumbers</code></p><p id="kb1uvd_12">Ends an ongoing flash call by specifying the originating and destination numbers.</p></section><section class="chapter"><h2 id="authentication" data-toc="authentication">Authentication</h2><p id="kb1uvd_14">Security requirements:</p><div class="table-wrapper"><table class="wide" id="kb1uvd_15"><thead><tr class="ijRowHead" id="kb1uvd_16"><th id="kb1uvd_18"><p>Scheme</p></th><th id="kb1uvd_19"><p>Scopes</p></th></tr></thead><tbody><tr id="kb1uvd_17"><td id="kb1uvd_20"><p>BearerAuth</p></td><td id="kb1uvd_21"><p>-</p></td></tr></tbody></table></div></section><section class="chapter"><h2 id="parameters" data-toc="parameters">Parameters</h2><div class="table-wrapper"><table class="wide" id="kb1uvd_22"><thead><tr class="ijRowHead" id="kb1uvd_23"><th id="kb1uvd_26"><p>Name</p></th><th id="kb1uvd_27"><p>In</p></th><th id="kb1uvd_28"><p>Required</p></th><th id="kb1uvd_29"><p>Type</p></th><th id="kb1uvd_30"><p>Description</p></th></tr></thead><tbody><tr id="kb1uvd_24"><td id="kb1uvd_31"><p>workspaceId</p></td><td id="kb1uvd_32"><p>query</p></td><td id="kb1uvd_33"><p>no</p></td><td id="kb1uvd_34"><p>string</p></td><td id="kb1uvd_35"><p>Bird Workspace identifier (falls back to module configuration if omitted)</p></td></tr><tr id="kb1uvd_25"><td id="kb1uvd_36"><p>channelId</p></td><td id="kb1uvd_37"><p>query</p></td><td id="kb1uvd_38"><p>no</p></td><td id="kb1uvd_39"><p>string</p></td><td id="kb1uvd_40"><p>Bird Channel identifier (falls back to module configuration if omitted)</p></td></tr></tbody></table></div></section><section class="chapter"><h2 id="request-body" data-toc="request-body">Request Body</h2><p id="kb1uvd_41">Required: yes.</p><p id="kb1uvd_42">Content type: <code class="code" id="kb1uvd_44">application/json</code></p><div class="code-block" data-lang="json">
{
&quot;additionalProperties&quot;: true,
&quot;properties&quot;: {
&quot;from&quot;: {
&quot;description&quot;: &quot;E.164 formatted caller number&quot;,
&quot;example&quot;: &quot;+4599988877&quot;,
&quot;type&quot;: &quot;string&quot;
},
&quot;to&quot;: {
&quot;description&quot;: &quot;E.164 formatted callee number&quot;,
&quot;example&quot;: &quot;+4511122233&quot;,
&quot;type&quot;: &quot;string&quot;
}
},
&quot;type&quot;: &quot;object&quot;
}
</div></section><section class="chapter"><h2 id="responses" data-toc="responses">Responses</h2><div class="table-wrapper"><table class="wide" id="kb1uvd_45"><thead><tr class="ijRowHead" id="kb1uvd_48"><th id="kb1uvd_50"><p>Status</p></th><th id="kb1uvd_51"><p>Description</p></th><th id="kb1uvd_52"><p>Content Types</p></th></tr></thead><tbody><tr id="kb1uvd_49"><td id="kb1uvd_53"><p>200</p></td><td id="kb1uvd_54"><p>Flash call completed (by numbers)</p></td><td id="kb1uvd_55"><p>application/json</p></td></tr></tbody></table></div><p id="kb1uvd_46">Schema for response <code class="code" id="kb1uvd_56">200</code> (<code class="code" id="kb1uvd_57">application/json</code>):</p><div class="code-block" data-lang="json">
{
&quot;additionalProperties&quot;: true,
&quot;type&quot;: &quot;object&quot;
}
</div></section><div class="last-modified">17 March 2026</div><div data-feedback-placeholder="true"></div><div class="navigation-links _bottom"><a href="birdcreateflashcall.html" class="navigation-links__prev">Create/place a flash call via Bird</a><a href="birdgetflashcall.html" class="navigation-links__next">Get a flash call by ID</a></div></article><div id="disqus_thread"></div></div></section></main></div><script src="https://resources.jetbrains.com/writerside/apidoc/6.26.0-b913/app.js"></script></body></html>
@@ -0,0 +1,21 @@
<!DOCTYPE html SYSTEM "about:legacy-compat">
<html lang="en-US" data-preset="contrast" data-primary-color="#307FFF"><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8"><meta charset="UTF-8"><meta name="robots" content="noindex"><meta name="built-on" content="2026-03-17T15:44:52.529316983"><title>Get a flash call by ID | Copenhagen Truck Wash API</title><script type="application/json" id="virtual-toc-data">[{"id":"endpoint","level":0,"title":"Endpoint","anchor":"#endpoint"},{"id":"operation","level":0,"title":"Operation","anchor":"#operation"},{"id":"authentication","level":0,"title":"Authentication","anchor":"#authentication"},{"id":"parameters","level":0,"title":"Parameters","anchor":"#parameters"},{"id":"responses","level":0,"title":"Responses","anchor":"#responses"}]</script><script type="application/json" id="topic-shortcuts"></script><link href="https://resources.jetbrains.com/writerside/apidoc/6.26.0-b913/app.css" rel="stylesheet"><meta name="msapplication-TileColor" content="#000000"><link rel="apple-touch-icon" sizes="180x180" href="https://jetbrains.com/apple-touch-icon.png"><link rel="icon" type="image/png" sizes="32x32" href="https://jetbrains.com/favicon-32x32.png"><link rel="icon" type="image/png" sizes="16x16" href="https://jetbrains.com/favicon-16x16.png"><meta name="msapplication-TileImage" content="https://resources.jetbrains.com/storage/ui/favicons/mstile-144x144.png"><meta name="msapplication-square70x70logo" content="https://resources.jetbrains.com/storage/ui/favicons/mstile-70x70.png"><meta name="msapplication-square150x150logo" content="https://resources.jetbrains.com/storage/ui/favicons/mstile-150x150.png"><meta name="msapplication-wide310x150logo" content="https://resources.jetbrains.com/storage/ui/favicons/mstile-310x150.png"><meta name="msapplication-square310x310logo" content="https://resources.jetbrains.com/storage/ui/favicons/mstile-310x310.png"><meta name="image" content=""><!-- Open Graph --><meta property="og:title" content="Get a flash call by ID | Copenhagen Truck Wash API"><meta property="og:description" content=""><meta property="og:image" content=""><meta property="og:site_name" content="Copenhagen Truck Wash API Help"><meta property="og:type" content="website"><meta property="og:locale" content="en_US"><meta property="og:url" content="writerside-documentation/birdgetflashcall.html"><!-- End Open Graph --><!-- Twitter Card --><meta name="twitter:card" content="summary_large_image"><meta name="twitter:site" content=""><meta name="twitter:title" content="Get a flash call by ID | Copenhagen Truck Wash API"><meta name="twitter:description" content=""><meta name="twitter:creator" content=""><meta name="twitter:image:src" content=""><!-- End Twitter Card --><!-- Schema.org WebPage --><script type="application/ld+json">{
"@context": "http://schema.org",
"@type": "WebPage",
"@id": "writerside-documentation/birdgetflashcall.html#webpage",
"url": "writerside-documentation/birdgetflashcall.html",
"name": "Get a flash call by ID | Copenhagen Truck Wash API",
"description": "",
"image": "",
"inLanguage":"en-US"
}</script><!-- End Schema.org --><!-- Schema.org WebSite --><script type="application/ld+json">{
"@type": "WebSite",
"@id": "writerside-documentation/#website",
"url": "writerside-documentation/",
"name": "Copenhagen Truck Wash API Help"
}</script><!-- End Schema.org --></head><body data-id="birdGetFlashCall" data-main-title="Get a flash call by ID" data-article-props="{&quot;seeAlsoStyle&quot;:&quot;links&quot;}" data-template="article" data-breadcrumbs="API-Reference.topic|API Reference///Tag_Bird.topic|Bird///Tag_Bird_Page_1.topic|Bird - Page 1 of 1"><div class="wrapper"><main class="panel _main"><header class="panel__header"><div class="container"><h3>Copenhagen Truck Wash API Help</h3><div class="panel-trigger"></div></div></header><section class="panel__content"><div class="container"><article class="article" data-shortcut-switcher="inactive"><h1 data-toc="birdGetFlashCall" id="birdGetFlashCall.topic">Get a flash call by ID</h1><p id="z2r9635_2">This endpoint documentation is generated directly from <code class="code" id="z2r9635_8">openapi.yaml</code>.</p><section class="chapter"><h2 id="endpoint" data-toc="endpoint">Endpoint</h2><div class="code-block" data-lang="http">GET /bird/voice/flash-calls/{id}</div></section><section class="chapter"><h2 id="operation" data-toc="operation">Operation</h2><p id="z2r9635_10">Operation ID: <code class="code" id="z2r9635_12">birdGetFlashCall</code></p><p id="z2r9635_11">Get a flash call by ID</p></section><section class="chapter"><h2 id="authentication" data-toc="authentication">Authentication</h2><p id="z2r9635_13">Security requirements:</p><div class="table-wrapper"><table class="wide" id="z2r9635_14"><thead><tr class="ijRowHead" id="z2r9635_15"><th id="z2r9635_17"><p>Scheme</p></th><th id="z2r9635_18"><p>Scopes</p></th></tr></thead><tbody><tr id="z2r9635_16"><td id="z2r9635_19"><p>BearerAuth</p></td><td id="z2r9635_20"><p>-</p></td></tr></tbody></table></div></section><section class="chapter"><h2 id="parameters" data-toc="parameters">Parameters</h2><div class="table-wrapper"><table class="wide" id="z2r9635_21"><thead><tr class="ijRowHead" id="z2r9635_22"><th id="z2r9635_26"><p>Name</p></th><th id="z2r9635_27"><p>In</p></th><th id="z2r9635_28"><p>Required</p></th><th id="z2r9635_29"><p>Type</p></th><th id="z2r9635_30"><p>Description</p></th></tr></thead><tbody><tr id="z2r9635_23"><td id="z2r9635_31"><p>workspaceId</p></td><td id="z2r9635_32"><p>query</p></td><td id="z2r9635_33"><p>no</p></td><td id="z2r9635_34"><p>string</p></td><td id="z2r9635_35"><p>Bird Workspace identifier (falls back to module configuration if omitted)</p></td></tr><tr id="z2r9635_24"><td id="z2r9635_36"><p>channelId</p></td><td id="z2r9635_37"><p>query</p></td><td id="z2r9635_38"><p>no</p></td><td id="z2r9635_39"><p>string</p></td><td id="z2r9635_40"><p>Bird Channel identifier (falls back to module configuration if omitted)</p></td></tr><tr id="z2r9635_25"><td id="z2r9635_41"><p>id</p></td><td id="z2r9635_42"><p>path</p></td><td id="z2r9635_43"><p>yes</p></td><td id="z2r9635_44"><p>string</p></td><td id="z2r9635_45"></td></tr></tbody></table></div></section><section class="chapter"><h2 id="responses" data-toc="responses">Responses</h2><div class="table-wrapper"><table class="wide" id="z2r9635_46"><thead><tr class="ijRowHead" id="z2r9635_49"><th id="z2r9635_51"><p>Status</p></th><th id="z2r9635_52"><p>Description</p></th><th id="z2r9635_53"><p>Content Types</p></th></tr></thead><tbody><tr id="z2r9635_50"><td id="z2r9635_54"><p>200</p></td><td id="z2r9635_55"><p>Flash call details</p></td><td id="z2r9635_56"><p>application/json</p></td></tr></tbody></table></div><p id="z2r9635_47">Schema for response <code class="code" id="z2r9635_57">200</code> (<code class="code" id="z2r9635_58">application/json</code>):</p><div class="code-block" data-lang="json">
{
&quot;additionalProperties&quot;: true,
&quot;type&quot;: &quot;object&quot;
}
</div></section><div class="last-modified">17 March 2026</div><div data-feedback-placeholder="true"></div><div class="navigation-links _bottom"><a href="birdendflashcallbynumbers.html" class="navigation-links__prev">Complete/end a flash call using from/to numbers</a><a href="birdendflashcall.html" class="navigation-links__next">Complete/end a flash call by ID</a></div></article><div id="disqus_thread"></div></div></section></main></div><script src="https://resources.jetbrains.com/writerside/apidoc/6.26.0-b913/app.js"></script></body></html>
@@ -0,0 +1,20 @@
<!DOCTYPE html SYSTEM "about:legacy-compat">
<html lang="en-US" data-preset="contrast" data-primary-color="#307FFF"><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8"><meta charset="UTF-8"><meta name="robots" content="noindex"><meta name="built-on" content="2026-03-17T15:41:46.223981425"><title>Get a number by ID | Copenhagen Truck Wash API</title><script type="application/json" id="virtual-toc-data">[{"id":"endpoint","level":0,"title":"Endpoint","anchor":"#endpoint"},{"id":"operation","level":0,"title":"Operation","anchor":"#operation"},{"id":"authentication","level":0,"title":"Authentication","anchor":"#authentication"},{"id":"parameters","level":0,"title":"Parameters","anchor":"#parameters"},{"id":"responses","level":0,"title":"Responses","anchor":"#responses"}]</script><script type="application/json" id="topic-shortcuts"></script><link href="https://resources.jetbrains.com/writerside/apidoc/6.26.0-b913/app.css" rel="stylesheet"><meta name="msapplication-TileColor" content="#000000"><link rel="apple-touch-icon" sizes="180x180" href="https://jetbrains.com/apple-touch-icon.png"><link rel="icon" type="image/png" sizes="32x32" href="https://jetbrains.com/favicon-32x32.png"><link rel="icon" type="image/png" sizes="16x16" href="https://jetbrains.com/favicon-16x16.png"><meta name="msapplication-TileImage" content="https://resources.jetbrains.com/storage/ui/favicons/mstile-144x144.png"><meta name="msapplication-square70x70logo" content="https://resources.jetbrains.com/storage/ui/favicons/mstile-70x70.png"><meta name="msapplication-square150x150logo" content="https://resources.jetbrains.com/storage/ui/favicons/mstile-150x150.png"><meta name="msapplication-wide310x150logo" content="https://resources.jetbrains.com/storage/ui/favicons/mstile-310x150.png"><meta name="msapplication-square310x310logo" content="https://resources.jetbrains.com/storage/ui/favicons/mstile-310x310.png"><meta name="image" content=""><!-- Open Graph --><meta property="og:title" content="Get a number by ID | Copenhagen Truck Wash API"><meta property="og:description" content=""><meta property="og:image" content=""><meta property="og:site_name" content="Copenhagen Truck Wash API Help"><meta property="og:type" content="website"><meta property="og:locale" content="en_US"><meta property="og:url" content="writerside-documentation/birdgetnumber.html"><!-- End Open Graph --><!-- Twitter Card --><meta name="twitter:card" content="summary_large_image"><meta name="twitter:site" content=""><meta name="twitter:title" content="Get a number by ID | Copenhagen Truck Wash API"><meta name="twitter:description" content=""><meta name="twitter:creator" content=""><meta name="twitter:image:src" content=""><!-- End Twitter Card --><!-- Schema.org WebPage --><script type="application/ld+json">{
"@context": "http://schema.org",
"@type": "WebPage",
"@id": "writerside-documentation/birdgetnumber.html#webpage",
"url": "writerside-documentation/birdgetnumber.html",
"name": "Get a number by ID | Copenhagen Truck Wash API",
"description": "",
"image": "",
"inLanguage":"en-US"
}</script><!-- End Schema.org --><!-- Schema.org WebSite --><script type="application/ld+json">{
"@type": "WebSite",
"@id": "writerside-documentation/#website",
"url": "writerside-documentation/",
"name": "Copenhagen Truck Wash API Help"
}</script><!-- End Schema.org --></head><body data-id="birdGetNumber" data-main-title="Get a number by ID" data-article-props="{&quot;seeAlsoStyle&quot;:&quot;links&quot;}" data-template="article" data-breadcrumbs="API-Reference.topic|API Reference///Tag_Bird.topic|Bird///Tag_Bird_Page_1.topic|Bird - Page 1 of 1"><div class="wrapper"><main class="panel _main"><header class="panel__header"><div class="container"><h3>Copenhagen Truck Wash API Help</h3><div class="panel-trigger"></div></div></header><section class="panel__content"><div class="container"><article class="article" data-shortcut-switcher="inactive"><h1 data-toc="birdGetNumber" id="birdGetNumber.topic">Get a number by ID</h1><p id="-xcdihk_2">This endpoint documentation is generated directly from <code class="code" id="-xcdihk_8">openapi.yaml</code>.</p><section class="chapter"><h2 id="endpoint" data-toc="endpoint">Endpoint</h2><div class="code-block" data-lang="http">GET /bird/numbers/{id}</div></section><section class="chapter"><h2 id="operation" data-toc="operation">Operation</h2><p id="-xcdihk_10">Operation ID: <code class="code" id="-xcdihk_12">birdGetNumber</code></p><p id="-xcdihk_11">Get a number by ID</p></section><section class="chapter"><h2 id="authentication" data-toc="authentication">Authentication</h2><p id="-xcdihk_13">Security requirements:</p><div class="table-wrapper"><table class="wide" id="-xcdihk_14"><thead><tr class="ijRowHead" id="-xcdihk_15"><th id="-xcdihk_17"><p>Scheme</p></th><th id="-xcdihk_18"><p>Scopes</p></th></tr></thead><tbody><tr id="-xcdihk_16"><td id="-xcdihk_19"><p>BearerAuth</p></td><td id="-xcdihk_20"><p>-</p></td></tr></tbody></table></div></section><section class="chapter"><h2 id="parameters" data-toc="parameters">Parameters</h2><div class="table-wrapper"><table class="wide" id="-xcdihk_21"><thead><tr class="ijRowHead" id="-xcdihk_22"><th id="-xcdihk_25"><p>Name</p></th><th id="-xcdihk_26"><p>In</p></th><th id="-xcdihk_27"><p>Required</p></th><th id="-xcdihk_28"><p>Type</p></th><th id="-xcdihk_29"><p>Description</p></th></tr></thead><tbody><tr id="-xcdihk_23"><td id="-xcdihk_30"><p>workspaceId</p></td><td id="-xcdihk_31"><p>query</p></td><td id="-xcdihk_32"><p>no</p></td><td id="-xcdihk_33"><p>string</p></td><td id="-xcdihk_34"><p>Bird Workspace identifier (optional if configured)</p></td></tr><tr id="-xcdihk_24"><td id="-xcdihk_35"><p>id</p></td><td id="-xcdihk_36"><p>path</p></td><td id="-xcdihk_37"><p>yes</p></td><td id="-xcdihk_38"><p>string</p></td><td id="-xcdihk_39"></td></tr></tbody></table></div></section><section class="chapter"><h2 id="responses" data-toc="responses">Responses</h2><div class="table-wrapper"><table class="wide" id="-xcdihk_40"><thead><tr class="ijRowHead" id="-xcdihk_43"><th id="-xcdihk_45"><p>Status</p></th><th id="-xcdihk_46"><p>Description</p></th><th id="-xcdihk_47"><p>Content Types</p></th></tr></thead><tbody><tr id="-xcdihk_44"><td id="-xcdihk_48"><p>200</p></td><td id="-xcdihk_49"><p>Number details</p></td><td id="-xcdihk_50"><p>application/json</p></td></tr></tbody></table></div><p id="-xcdihk_41">Schema for response <code class="code" id="-xcdihk_51">200</code> (<code class="code" id="-xcdihk_52">application/json</code>):</p><div class="code-block" data-lang="json">
{
&quot;$ref&quot;: &quot;#/components/schemas/BirdNumberSingleResponse&quot;
}
</div></section><div class="last-modified">17 March 2026</div><div data-feedback-placeholder="true"></div><div class="navigation-links _bottom"><a href="birddeletenumber.html" class="navigation-links__prev">Delete/release a number by ID</a><a href="birdlistvoicecalls.html" class="navigation-links__next">List voice calls</a></div></article><div id="disqus_thread"></div></div></section></main></div><script src="https://resources.jetbrains.com/writerside/apidoc/6.26.0-b913/app.js"></script></body></html>
@@ -0,0 +1,20 @@
<!DOCTYPE html SYSTEM "about:legacy-compat">
<html lang="en-US" data-preset="contrast" data-primary-color="#307FFF"><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8"><meta charset="UTF-8"><meta name="robots" content="noindex"><meta name="built-on" content="2026-03-17T15:42:31.03654499"><title>Get a voice call by ID | Copenhagen Truck Wash API</title><script type="application/json" id="virtual-toc-data">[{"id":"endpoint","level":0,"title":"Endpoint","anchor":"#endpoint"},{"id":"operation","level":0,"title":"Operation","anchor":"#operation"},{"id":"authentication","level":0,"title":"Authentication","anchor":"#authentication"},{"id":"parameters","level":0,"title":"Parameters","anchor":"#parameters"},{"id":"responses","level":0,"title":"Responses","anchor":"#responses"}]</script><script type="application/json" id="topic-shortcuts"></script><link href="https://resources.jetbrains.com/writerside/apidoc/6.26.0-b913/app.css" rel="stylesheet"><meta name="msapplication-TileColor" content="#000000"><link rel="apple-touch-icon" sizes="180x180" href="https://jetbrains.com/apple-touch-icon.png"><link rel="icon" type="image/png" sizes="32x32" href="https://jetbrains.com/favicon-32x32.png"><link rel="icon" type="image/png" sizes="16x16" href="https://jetbrains.com/favicon-16x16.png"><meta name="msapplication-TileImage" content="https://resources.jetbrains.com/storage/ui/favicons/mstile-144x144.png"><meta name="msapplication-square70x70logo" content="https://resources.jetbrains.com/storage/ui/favicons/mstile-70x70.png"><meta name="msapplication-square150x150logo" content="https://resources.jetbrains.com/storage/ui/favicons/mstile-150x150.png"><meta name="msapplication-wide310x150logo" content="https://resources.jetbrains.com/storage/ui/favicons/mstile-310x150.png"><meta name="msapplication-square310x310logo" content="https://resources.jetbrains.com/storage/ui/favicons/mstile-310x310.png"><meta name="image" content=""><!-- Open Graph --><meta property="og:title" content="Get a voice call by ID | Copenhagen Truck Wash API"><meta property="og:description" content=""><meta property="og:image" content=""><meta property="og:site_name" content="Copenhagen Truck Wash API Help"><meta property="og:type" content="website"><meta property="og:locale" content="en_US"><meta property="og:url" content="writerside-documentation/birdgetvoicecall.html"><!-- End Open Graph --><!-- Twitter Card --><meta name="twitter:card" content="summary_large_image"><meta name="twitter:site" content=""><meta name="twitter:title" content="Get a voice call by ID | Copenhagen Truck Wash API"><meta name="twitter:description" content=""><meta name="twitter:creator" content=""><meta name="twitter:image:src" content=""><!-- End Twitter Card --><!-- Schema.org WebPage --><script type="application/ld+json">{
"@context": "http://schema.org",
"@type": "WebPage",
"@id": "writerside-documentation/birdgetvoicecall.html#webpage",
"url": "writerside-documentation/birdgetvoicecall.html",
"name": "Get a voice call by ID | Copenhagen Truck Wash API",
"description": "",
"image": "",
"inLanguage":"en-US"
}</script><!-- End Schema.org --><!-- Schema.org WebSite --><script type="application/ld+json">{
"@type": "WebSite",
"@id": "writerside-documentation/#website",
"url": "writerside-documentation/",
"name": "Copenhagen Truck Wash API Help"
}</script><!-- End Schema.org --></head><body data-id="birdGetVoiceCall" data-main-title="Get a voice call by ID" data-article-props="{&quot;seeAlsoStyle&quot;:&quot;links&quot;}" data-template="article" data-breadcrumbs="API-Reference.topic|API Reference///Tag_Bird.topic|Bird///Tag_Bird_Page_1.topic|Bird - Page 1 of 1"><div class="wrapper"><main class="panel _main"><header class="panel__header"><div class="container"><h3>Copenhagen Truck Wash API Help</h3><div class="panel-trigger"></div></div></header><section class="panel__content"><div class="container"><article class="article" data-shortcut-switcher="inactive"><h1 data-toc="birdGetVoiceCall" id="birdGetVoiceCall.topic">Get a voice call by ID</h1><p id="-xf17hp_2">This endpoint documentation is generated directly from <code class="code" id="-xf17hp_8">openapi.yaml</code>.</p><section class="chapter"><h2 id="endpoint" data-toc="endpoint">Endpoint</h2><div class="code-block" data-lang="http">GET /bird/voice/calls/{id}</div></section><section class="chapter"><h2 id="operation" data-toc="operation">Operation</h2><p id="-xf17hp_10">Operation ID: <code class="code" id="-xf17hp_12">birdGetVoiceCall</code></p><p id="-xf17hp_11">Get a voice call by ID</p></section><section class="chapter"><h2 id="authentication" data-toc="authentication">Authentication</h2><p id="-xf17hp_13">Security requirements:</p><div class="table-wrapper"><table class="wide" id="-xf17hp_14"><thead><tr class="ijRowHead" id="-xf17hp_15"><th id="-xf17hp_17"><p>Scheme</p></th><th id="-xf17hp_18"><p>Scopes</p></th></tr></thead><tbody><tr id="-xf17hp_16"><td id="-xf17hp_19"><p>BearerAuth</p></td><td id="-xf17hp_20"><p>-</p></td></tr></tbody></table></div></section><section class="chapter"><h2 id="parameters" data-toc="parameters">Parameters</h2><div class="table-wrapper"><table class="wide" id="-xf17hp_21"><thead><tr class="ijRowHead" id="-xf17hp_22"><th id="-xf17hp_26"><p>Name</p></th><th id="-xf17hp_27"><p>In</p></th><th id="-xf17hp_28"><p>Required</p></th><th id="-xf17hp_29"><p>Type</p></th><th id="-xf17hp_30"><p>Description</p></th></tr></thead><tbody><tr id="-xf17hp_23"><td id="-xf17hp_31"><p>workspaceId</p></td><td id="-xf17hp_32"><p>query</p></td><td id="-xf17hp_33"><p>no</p></td><td id="-xf17hp_34"><p>string</p></td><td id="-xf17hp_35"><p>Bird Workspace identifier (falls back to module configuration if omitted)</p></td></tr><tr id="-xf17hp_24"><td id="-xf17hp_36"><p>channelId</p></td><td id="-xf17hp_37"><p>query</p></td><td id="-xf17hp_38"><p>no</p></td><td id="-xf17hp_39"><p>string</p></td><td id="-xf17hp_40"><p>Bird Channel identifier (falls back to module configuration if omitted)</p></td></tr><tr id="-xf17hp_25"><td id="-xf17hp_41"><p>id</p></td><td id="-xf17hp_42"><p>path</p></td><td id="-xf17hp_43"><p>yes</p></td><td id="-xf17hp_44"><p>string</p></td><td id="-xf17hp_45"></td></tr></tbody></table></div></section><section class="chapter"><h2 id="responses" data-toc="responses">Responses</h2><div class="table-wrapper"><table class="wide" id="-xf17hp_46"><thead><tr class="ijRowHead" id="-xf17hp_49"><th id="-xf17hp_51"><p>Status</p></th><th id="-xf17hp_52"><p>Description</p></th><th id="-xf17hp_53"><p>Content Types</p></th></tr></thead><tbody><tr id="-xf17hp_50"><td id="-xf17hp_54"><p>200</p></td><td id="-xf17hp_55"><p>Call details</p></td><td id="-xf17hp_56"><p>application/json</p></td></tr></tbody></table></div><p id="-xf17hp_47">Schema for response <code class="code" id="-xf17hp_57">200</code> (<code class="code" id="-xf17hp_58">application/json</code>):</p><div class="code-block" data-lang="json">
{
&quot;$ref&quot;: &quot;#/components/schemas/BirdVoiceCallSingleResponse&quot;
}
</div></section><div class="last-modified">17 March 2026</div><div data-feedback-placeholder="true"></div><div class="navigation-links _bottom"><a href="birdtestoutboundvoicecall.html" class="navigation-links__prev">Place a test outbound call and hang up when accepted</a><a href="birdhangupvoicecall.html" class="navigation-links__next">Hang up a voice call by ID</a></div></article><div id="disqus_thread"></div></div></section></main></div><script src="https://resources.jetbrains.com/writerside/apidoc/6.26.0-b913/app.js"></script></body></html>
@@ -0,0 +1,20 @@
<!DOCTYPE html SYSTEM "about:legacy-compat">
<html lang="en-US" data-preset="contrast" data-primary-color="#307FFF"><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8"><meta charset="UTF-8"><meta name="robots" content="noindex"><meta name="built-on" content="2026-03-17T15:43:00.58965386"><title>Hang up a voice call by ID | Copenhagen Truck Wash API</title><script type="application/json" id="virtual-toc-data">[{"id":"endpoint","level":0,"title":"Endpoint","anchor":"#endpoint"},{"id":"operation","level":0,"title":"Operation","anchor":"#operation"},{"id":"authentication","level":0,"title":"Authentication","anchor":"#authentication"},{"id":"parameters","level":0,"title":"Parameters","anchor":"#parameters"},{"id":"responses","level":0,"title":"Responses","anchor":"#responses"}]</script><script type="application/json" id="topic-shortcuts"></script><link href="https://resources.jetbrains.com/writerside/apidoc/6.26.0-b913/app.css" rel="stylesheet"><meta name="msapplication-TileColor" content="#000000"><link rel="apple-touch-icon" sizes="180x180" href="https://jetbrains.com/apple-touch-icon.png"><link rel="icon" type="image/png" sizes="32x32" href="https://jetbrains.com/favicon-32x32.png"><link rel="icon" type="image/png" sizes="16x16" href="https://jetbrains.com/favicon-16x16.png"><meta name="msapplication-TileImage" content="https://resources.jetbrains.com/storage/ui/favicons/mstile-144x144.png"><meta name="msapplication-square70x70logo" content="https://resources.jetbrains.com/storage/ui/favicons/mstile-70x70.png"><meta name="msapplication-square150x150logo" content="https://resources.jetbrains.com/storage/ui/favicons/mstile-150x150.png"><meta name="msapplication-wide310x150logo" content="https://resources.jetbrains.com/storage/ui/favicons/mstile-310x150.png"><meta name="msapplication-square310x310logo" content="https://resources.jetbrains.com/storage/ui/favicons/mstile-310x310.png"><meta name="image" content=""><!-- Open Graph --><meta property="og:title" content="Hang up a voice call by ID | Copenhagen Truck Wash API"><meta property="og:description" content=""><meta property="og:image" content=""><meta property="og:site_name" content="Copenhagen Truck Wash API Help"><meta property="og:type" content="website"><meta property="og:locale" content="en_US"><meta property="og:url" content="writerside-documentation/birdhangupvoicecall.html"><!-- End Open Graph --><!-- Twitter Card --><meta name="twitter:card" content="summary_large_image"><meta name="twitter:site" content=""><meta name="twitter:title" content="Hang up a voice call by ID | Copenhagen Truck Wash API"><meta name="twitter:description" content=""><meta name="twitter:creator" content=""><meta name="twitter:image:src" content=""><!-- End Twitter Card --><!-- Schema.org WebPage --><script type="application/ld+json">{
"@context": "http://schema.org",
"@type": "WebPage",
"@id": "writerside-documentation/birdhangupvoicecall.html#webpage",
"url": "writerside-documentation/birdhangupvoicecall.html",
"name": "Hang up a voice call by ID | Copenhagen Truck Wash API",
"description": "",
"image": "",
"inLanguage":"en-US"
}</script><!-- End Schema.org --><!-- Schema.org WebSite --><script type="application/ld+json">{
"@type": "WebSite",
"@id": "writerside-documentation/#website",
"url": "writerside-documentation/",
"name": "Copenhagen Truck Wash API Help"
}</script><!-- End Schema.org --></head><body data-id="birdHangupVoiceCall" data-main-title="Hang up a voice call by ID" data-article-props="{&quot;seeAlsoStyle&quot;:&quot;links&quot;}" data-template="article" data-breadcrumbs="API-Reference.topic|API Reference///Tag_Bird.topic|Bird///Tag_Bird_Page_1.topic|Bird - Page 1 of 1"><div class="wrapper"><main class="panel _main"><header class="panel__header"><div class="container"><h3>Copenhagen Truck Wash API Help</h3><div class="panel-trigger"></div></div></header><section class="panel__content"><div class="container"><article class="article" data-shortcut-switcher="inactive"><h1 data-toc="birdHangupVoiceCall" id="birdHangupVoiceCall.topic">Hang up a voice call by ID</h1><p id="-b4pvg4_2">This endpoint documentation is generated directly from <code class="code" id="-b4pvg4_8">openapi.yaml</code>.</p><section class="chapter"><h2 id="endpoint" data-toc="endpoint">Endpoint</h2><div class="code-block" data-lang="http">POST /bird/voice/calls/{id}/hangup</div></section><section class="chapter"><h2 id="operation" data-toc="operation">Operation</h2><p id="-b4pvg4_10">Operation ID: <code class="code" id="-b4pvg4_12">birdHangupVoiceCall</code></p><p id="-b4pvg4_11">Hang up a voice call by ID</p></section><section class="chapter"><h2 id="authentication" data-toc="authentication">Authentication</h2><p id="-b4pvg4_13">Security requirements:</p><div class="table-wrapper"><table class="wide" id="-b4pvg4_14"><thead><tr class="ijRowHead" id="-b4pvg4_15"><th id="-b4pvg4_17"><p>Scheme</p></th><th id="-b4pvg4_18"><p>Scopes</p></th></tr></thead><tbody><tr id="-b4pvg4_16"><td id="-b4pvg4_19"><p>BearerAuth</p></td><td id="-b4pvg4_20"><p>-</p></td></tr></tbody></table></div></section><section class="chapter"><h2 id="parameters" data-toc="parameters">Parameters</h2><div class="table-wrapper"><table class="wide" id="-b4pvg4_21"><thead><tr class="ijRowHead" id="-b4pvg4_22"><th id="-b4pvg4_26"><p>Name</p></th><th id="-b4pvg4_27"><p>In</p></th><th id="-b4pvg4_28"><p>Required</p></th><th id="-b4pvg4_29"><p>Type</p></th><th id="-b4pvg4_30"><p>Description</p></th></tr></thead><tbody><tr id="-b4pvg4_23"><td id="-b4pvg4_31"><p>workspaceId</p></td><td id="-b4pvg4_32"><p>query</p></td><td id="-b4pvg4_33"><p>no</p></td><td id="-b4pvg4_34"><p>string</p></td><td id="-b4pvg4_35"><p>Bird Workspace identifier (falls back to module configuration if omitted)</p></td></tr><tr id="-b4pvg4_24"><td id="-b4pvg4_36"><p>channelId</p></td><td id="-b4pvg4_37"><p>query</p></td><td id="-b4pvg4_38"><p>no</p></td><td id="-b4pvg4_39"><p>string</p></td><td id="-b4pvg4_40"><p>Bird Channel identifier (falls back to module configuration if omitted)</p></td></tr><tr id="-b4pvg4_25"><td id="-b4pvg4_41"><p>id</p></td><td id="-b4pvg4_42"><p>path</p></td><td id="-b4pvg4_43"><p>yes</p></td><td id="-b4pvg4_44"><p>string</p></td><td id="-b4pvg4_45"></td></tr></tbody></table></div></section><section class="chapter"><h2 id="responses" data-toc="responses">Responses</h2><div class="table-wrapper"><table class="wide" id="-b4pvg4_46"><thead><tr class="ijRowHead" id="-b4pvg4_49"><th id="-b4pvg4_51"><p>Status</p></th><th id="-b4pvg4_52"><p>Description</p></th><th id="-b4pvg4_53"><p>Content Types</p></th></tr></thead><tbody><tr id="-b4pvg4_50"><td id="-b4pvg4_54"><p>200</p></td><td id="-b4pvg4_55"><p>Hangup requested</p></td><td id="-b4pvg4_56"><p>application/json</p></td></tr></tbody></table></div><p id="-b4pvg4_47">Schema for response <code class="code" id="-b4pvg4_57">200</code> (<code class="code" id="-b4pvg4_58">application/json</code>):</p><div class="code-block" data-lang="json">
{
&quot;$ref&quot;: &quot;#/components/schemas/BirdVoiceCallSingleResponse&quot;
}
</div></section><div class="last-modified">17 March 2026</div><div data-feedback-placeholder="true"></div><div class="navigation-links _bottom"><a href="birdgetvoicecall.html" class="navigation-links__prev">Get a voice call by ID</a><a href="birdsayonvoicecall.html" class="navigation-links__next">Say a message on an active voice call and hang up afterwards</a></div></article><div id="disqus_thread"></div></div></section></main></div><script src="https://resources.jetbrains.com/writerside/apidoc/6.26.0-b913/app.js"></script></body></html>
@@ -0,0 +1,21 @@
<!DOCTYPE html SYSTEM "about:legacy-compat">
<html lang="en-US" data-preset="contrast" data-primary-color="#307FFF"><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8"><meta charset="UTF-8"><meta name="robots" content="noindex"><meta name="built-on" content="2026-03-17T15:43:49.737344517"><title>List flash calls | Copenhagen Truck Wash API</title><script type="application/json" id="virtual-toc-data">[{"id":"endpoint","level":0,"title":"Endpoint","anchor":"#endpoint"},{"id":"operation","level":0,"title":"Operation","anchor":"#operation"},{"id":"authentication","level":0,"title":"Authentication","anchor":"#authentication"},{"id":"parameters","level":0,"title":"Parameters","anchor":"#parameters"},{"id":"responses","level":0,"title":"Responses","anchor":"#responses"}]</script><script type="application/json" id="topic-shortcuts"></script><link href="https://resources.jetbrains.com/writerside/apidoc/6.26.0-b913/app.css" rel="stylesheet"><meta name="msapplication-TileColor" content="#000000"><link rel="apple-touch-icon" sizes="180x180" href="https://jetbrains.com/apple-touch-icon.png"><link rel="icon" type="image/png" sizes="32x32" href="https://jetbrains.com/favicon-32x32.png"><link rel="icon" type="image/png" sizes="16x16" href="https://jetbrains.com/favicon-16x16.png"><meta name="msapplication-TileImage" content="https://resources.jetbrains.com/storage/ui/favicons/mstile-144x144.png"><meta name="msapplication-square70x70logo" content="https://resources.jetbrains.com/storage/ui/favicons/mstile-70x70.png"><meta name="msapplication-square150x150logo" content="https://resources.jetbrains.com/storage/ui/favicons/mstile-150x150.png"><meta name="msapplication-wide310x150logo" content="https://resources.jetbrains.com/storage/ui/favicons/mstile-310x150.png"><meta name="msapplication-square310x310logo" content="https://resources.jetbrains.com/storage/ui/favicons/mstile-310x310.png"><meta name="image" content=""><!-- Open Graph --><meta property="og:title" content="List flash calls | Copenhagen Truck Wash API"><meta property="og:description" content=""><meta property="og:image" content=""><meta property="og:site_name" content="Copenhagen Truck Wash API Help"><meta property="og:type" content="website"><meta property="og:locale" content="en_US"><meta property="og:url" content="writerside-documentation/birdlistflashcalls.html"><!-- End Open Graph --><!-- Twitter Card --><meta name="twitter:card" content="summary_large_image"><meta name="twitter:site" content=""><meta name="twitter:title" content="List flash calls | Copenhagen Truck Wash API"><meta name="twitter:description" content=""><meta name="twitter:creator" content=""><meta name="twitter:image:src" content=""><!-- End Twitter Card --><!-- Schema.org WebPage --><script type="application/ld+json">{
"@context": "http://schema.org",
"@type": "WebPage",
"@id": "writerside-documentation/birdlistflashcalls.html#webpage",
"url": "writerside-documentation/birdlistflashcalls.html",
"name": "List flash calls | Copenhagen Truck Wash API",
"description": "",
"image": "",
"inLanguage":"en-US"
}</script><!-- End Schema.org --><!-- Schema.org WebSite --><script type="application/ld+json">{
"@type": "WebSite",
"@id": "writerside-documentation/#website",
"url": "writerside-documentation/",
"name": "Copenhagen Truck Wash API Help"
}</script><!-- End Schema.org --></head><body data-id="birdListFlashCalls" data-main-title="List flash calls" data-article-props="{&quot;seeAlsoStyle&quot;:&quot;links&quot;}" data-template="article" data-breadcrumbs="API-Reference.topic|API Reference///Tag_Bird.topic|Bird///Tag_Bird_Page_1.topic|Bird - Page 1 of 1"><div class="wrapper"><main class="panel _main"><header class="panel__header"><div class="container"><h3>Copenhagen Truck Wash API Help</h3><div class="panel-trigger"></div></div></header><section class="panel__content"><div class="container"><article class="article" data-shortcut-switcher="inactive"><h1 data-toc="birdListFlashCalls" id="birdListFlashCalls.topic">List flash calls</h1><p id="dzggm4_2">This endpoint documentation is generated directly from <code class="code" id="dzggm4_8">openapi.yaml</code>.</p><section class="chapter"><h2 id="endpoint" data-toc="endpoint">Endpoint</h2><div class="code-block" data-lang="http">GET /bird/voice/flash-calls</div></section><section class="chapter"><h2 id="operation" data-toc="operation">Operation</h2><p id="dzggm4_10">Operation ID: <code class="code" id="dzggm4_12">birdListFlashCalls</code></p><p id="dzggm4_11">List flash calls</p></section><section class="chapter"><h2 id="authentication" data-toc="authentication">Authentication</h2><p id="dzggm4_13">Security requirements:</p><div class="table-wrapper"><table class="wide" id="dzggm4_14"><thead><tr class="ijRowHead" id="dzggm4_15"><th id="dzggm4_17"><p>Scheme</p></th><th id="dzggm4_18"><p>Scopes</p></th></tr></thead><tbody><tr id="dzggm4_16"><td id="dzggm4_19"><p>BearerAuth</p></td><td id="dzggm4_20"><p>-</p></td></tr></tbody></table></div></section><section class="chapter"><h2 id="parameters" data-toc="parameters">Parameters</h2><div class="table-wrapper"><table class="wide" id="dzggm4_21"><thead><tr class="ijRowHead" id="dzggm4_22"><th id="dzggm4_26"><p>Name</p></th><th id="dzggm4_27"><p>In</p></th><th id="dzggm4_28"><p>Required</p></th><th id="dzggm4_29"><p>Type</p></th><th id="dzggm4_30"><p>Description</p></th></tr></thead><tbody><tr id="dzggm4_23"><td id="dzggm4_31"><p>workspaceId</p></td><td id="dzggm4_32"><p>query</p></td><td id="dzggm4_33"><p>no</p></td><td id="dzggm4_34"><p>string</p></td><td id="dzggm4_35"><p>Bird Workspace identifier (falls back to module configuration if omitted)</p></td></tr><tr id="dzggm4_24"><td id="dzggm4_36"><p>channelId</p></td><td id="dzggm4_37"><p>query</p></td><td id="dzggm4_38"><p>no</p></td><td id="dzggm4_39"><p>string</p></td><td id="dzggm4_40"><p>Bird Channel identifier (falls back to module configuration if omitted)</p></td></tr><tr id="dzggm4_25"><td id="dzggm4_41"><p>page</p></td><td id="dzggm4_42"><p>query</p></td><td id="dzggm4_43"><p>no</p></td><td id="dzggm4_44"><p>integer</p></td><td id="dzggm4_45"></td></tr></tbody></table></div></section><section class="chapter"><h2 id="responses" data-toc="responses">Responses</h2><div class="table-wrapper"><table class="wide" id="dzggm4_46"><thead><tr class="ijRowHead" id="dzggm4_49"><th id="dzggm4_51"><p>Status</p></th><th id="dzggm4_52"><p>Description</p></th><th id="dzggm4_53"><p>Content Types</p></th></tr></thead><tbody><tr id="dzggm4_50"><td id="dzggm4_54"><p>200</p></td><td id="dzggm4_55"><p>A list of flash calls</p></td><td id="dzggm4_56"><p>application/json</p></td></tr></tbody></table></div><p id="dzggm4_47">Schema for response <code class="code" id="dzggm4_57">200</code> (<code class="code" id="dzggm4_58">application/json</code>):</p><div class="code-block" data-lang="json">
{
&quot;additionalProperties&quot;: true,
&quot;type&quot;: &quot;object&quot;
}
</div></section><div class="last-modified">17 March 2026</div><div data-feedback-placeholder="true"></div><div class="navigation-links _bottom"><a href="birdsayonvoicecall.html" class="navigation-links__prev">Say a message on an active voice call and hang up afterwards</a><a href="birdcreateflashcall.html" class="navigation-links__next">Create/place a flash call via Bird</a></div></article><div id="disqus_thread"></div></div></section></main></div><script src="https://resources.jetbrains.com/writerside/apidoc/6.26.0-b913/app.js"></script></body></html>
@@ -0,0 +1,20 @@
<!DOCTYPE html SYSTEM "about:legacy-compat">
<html lang="en-US" data-preset="contrast" data-primary-color="#307FFF"><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8"><meta charset="UTF-8"><meta name="robots" content="noindex"><meta name="built-on" content="2026-03-17T15:41:18.114543899"><title>List your numbers | Copenhagen Truck Wash API</title><script type="application/json" id="virtual-toc-data">[{"id":"endpoint","level":0,"title":"Endpoint","anchor":"#endpoint"},{"id":"operation","level":0,"title":"Operation","anchor":"#operation"},{"id":"authentication","level":0,"title":"Authentication","anchor":"#authentication"},{"id":"parameters","level":0,"title":"Parameters","anchor":"#parameters"},{"id":"responses","level":0,"title":"Responses","anchor":"#responses"}]</script><script type="application/json" id="topic-shortcuts"></script><link href="https://resources.jetbrains.com/writerside/apidoc/6.26.0-b913/app.css" rel="stylesheet"><meta name="msapplication-TileColor" content="#000000"><link rel="apple-touch-icon" sizes="180x180" href="https://jetbrains.com/apple-touch-icon.png"><link rel="icon" type="image/png" sizes="32x32" href="https://jetbrains.com/favicon-32x32.png"><link rel="icon" type="image/png" sizes="16x16" href="https://jetbrains.com/favicon-16x16.png"><meta name="msapplication-TileImage" content="https://resources.jetbrains.com/storage/ui/favicons/mstile-144x144.png"><meta name="msapplication-square70x70logo" content="https://resources.jetbrains.com/storage/ui/favicons/mstile-70x70.png"><meta name="msapplication-square150x150logo" content="https://resources.jetbrains.com/storage/ui/favicons/mstile-150x150.png"><meta name="msapplication-wide310x150logo" content="https://resources.jetbrains.com/storage/ui/favicons/mstile-310x150.png"><meta name="msapplication-square310x310logo" content="https://resources.jetbrains.com/storage/ui/favicons/mstile-310x310.png"><meta name="image" content=""><!-- Open Graph --><meta property="og:title" content="List your numbers | Copenhagen Truck Wash API"><meta property="og:description" content=""><meta property="og:image" content=""><meta property="og:site_name" content="Copenhagen Truck Wash API Help"><meta property="og:type" content="website"><meta property="og:locale" content="en_US"><meta property="og:url" content="writerside-documentation/birdlistnumbers.html"><!-- End Open Graph --><!-- Twitter Card --><meta name="twitter:card" content="summary_large_image"><meta name="twitter:site" content=""><meta name="twitter:title" content="List your numbers | Copenhagen Truck Wash API"><meta name="twitter:description" content=""><meta name="twitter:creator" content=""><meta name="twitter:image:src" content=""><!-- End Twitter Card --><!-- Schema.org WebPage --><script type="application/ld+json">{
"@context": "http://schema.org",
"@type": "WebPage",
"@id": "writerside-documentation/birdlistnumbers.html#webpage",
"url": "writerside-documentation/birdlistnumbers.html",
"name": "List your numbers | Copenhagen Truck Wash API",
"description": "",
"image": "",
"inLanguage":"en-US"
}</script><!-- End Schema.org --><!-- Schema.org WebSite --><script type="application/ld+json">{
"@type": "WebSite",
"@id": "writerside-documentation/#website",
"url": "writerside-documentation/",
"name": "Copenhagen Truck Wash API Help"
}</script><!-- End Schema.org --></head><body data-id="birdListNumbers" data-main-title="List your numbers" data-article-props="{&quot;seeAlsoStyle&quot;:&quot;links&quot;}" data-template="article" data-breadcrumbs="API-Reference.topic|API Reference///Tag_Bird.topic|Bird///Tag_Bird_Page_1.topic|Bird - Page 1 of 1"><div class="wrapper"><main class="panel _main"><header class="panel__header"><div class="container"><h3>Copenhagen Truck Wash API Help</h3><div class="panel-trigger"></div></div></header><section class="panel__content"><div class="container"><article class="article" data-shortcut-switcher="inactive"><h1 data-toc="birdListNumbers" id="birdListNumbers.topic">List your numbers</h1><p id="z1kcp9x_2">This endpoint documentation is generated directly from <code class="code" id="z1kcp9x_8">openapi.yaml</code>.</p><section class="chapter"><h2 id="endpoint" data-toc="endpoint">Endpoint</h2><div class="code-block" data-lang="http">GET /bird/numbers</div></section><section class="chapter"><h2 id="operation" data-toc="operation">Operation</h2><p id="z1kcp9x_10">Operation ID: <code class="code" id="z1kcp9x_12">birdListNumbers</code></p><p id="z1kcp9x_11">List your numbers</p></section><section class="chapter"><h2 id="authentication" data-toc="authentication">Authentication</h2><p id="z1kcp9x_13">Security requirements:</p><div class="table-wrapper"><table class="wide" id="z1kcp9x_14"><thead><tr class="ijRowHead" id="z1kcp9x_15"><th id="z1kcp9x_17"><p>Scheme</p></th><th id="z1kcp9x_18"><p>Scopes</p></th></tr></thead><tbody><tr id="z1kcp9x_16"><td id="z1kcp9x_19"><p>BearerAuth</p></td><td id="z1kcp9x_20"><p>-</p></td></tr></tbody></table></div></section><section class="chapter"><h2 id="parameters" data-toc="parameters">Parameters</h2><div class="table-wrapper"><table class="wide" id="z1kcp9x_21"><thead><tr class="ijRowHead" id="z1kcp9x_22"><th id="z1kcp9x_26"><p>Name</p></th><th id="z1kcp9x_27"><p>In</p></th><th id="z1kcp9x_28"><p>Required</p></th><th id="z1kcp9x_29"><p>Type</p></th><th id="z1kcp9x_30"><p>Description</p></th></tr></thead><tbody><tr id="z1kcp9x_23"><td id="z1kcp9x_31"><p>workspaceId</p></td><td id="z1kcp9x_32"><p>query</p></td><td id="z1kcp9x_33"><p>no</p></td><td id="z1kcp9x_34"><p>string</p></td><td id="z1kcp9x_35"><p>Bird Workspace identifier (optional if configured)</p></td></tr><tr id="z1kcp9x_24"><td id="z1kcp9x_36"><p>page</p></td><td id="z1kcp9x_37"><p>query</p></td><td id="z1kcp9x_38"><p>no</p></td><td id="z1kcp9x_39"><p>integer</p></td><td id="z1kcp9x_40"></td></tr><tr id="z1kcp9x_25"><td id="z1kcp9x_41"><p>limit</p></td><td id="z1kcp9x_42"><p>query</p></td><td id="z1kcp9x_43"><p>no</p></td><td id="z1kcp9x_44"><p>integer</p></td><td id="z1kcp9x_45"></td></tr></tbody></table></div></section><section class="chapter"><h2 id="responses" data-toc="responses">Responses</h2><div class="table-wrapper"><table class="wide" id="z1kcp9x_46"><thead><tr class="ijRowHead" id="z1kcp9x_49"><th id="z1kcp9x_51"><p>Status</p></th><th id="z1kcp9x_52"><p>Description</p></th><th id="z1kcp9x_53"><p>Content Types</p></th></tr></thead><tbody><tr id="z1kcp9x_50"><td id="z1kcp9x_54"><p>200</p></td><td id="z1kcp9x_55"><p>A list of numbers</p></td><td id="z1kcp9x_56"><p>application/json</p></td></tr></tbody></table></div><p id="z1kcp9x_47">Schema for response <code class="code" id="z1kcp9x_57">200</code> (<code class="code" id="z1kcp9x_58">application/json</code>):</p><div class="code-block" data-lang="json">
{
&quot;$ref&quot;: &quot;#/components/schemas/BirdNumberListResponse&quot;
}
</div></section><div class="last-modified">17 March 2026</div><div data-feedback-placeholder="true"></div><div class="navigation-links _bottom"><a href="tag-bird-page-1.html" class="navigation-links__prev">Bird - Page 1 of 1</a><a href="birddeletenumber.html" class="navigation-links__next">Delete/release a number by ID</a></div></article><div id="disqus_thread"></div></div></section></main></div><script src="https://resources.jetbrains.com/writerside/apidoc/6.26.0-b913/app.js"></script></body></html>
@@ -0,0 +1,20 @@
<!DOCTYPE html SYSTEM "about:legacy-compat">
<html lang="en-US" data-preset="contrast" data-primary-color="#307FFF"><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8"><meta charset="UTF-8"><meta name="robots" content="noindex"><meta name="built-on" content="2026-03-17T15:42:00.622041611"><title>List voice calls | Copenhagen Truck Wash API</title><script type="application/json" id="virtual-toc-data">[{"id":"endpoint","level":0,"title":"Endpoint","anchor":"#endpoint"},{"id":"operation","level":0,"title":"Operation","anchor":"#operation"},{"id":"authentication","level":0,"title":"Authentication","anchor":"#authentication"},{"id":"parameters","level":0,"title":"Parameters","anchor":"#parameters"},{"id":"responses","level":0,"title":"Responses","anchor":"#responses"}]</script><script type="application/json" id="topic-shortcuts"></script><link href="https://resources.jetbrains.com/writerside/apidoc/6.26.0-b913/app.css" rel="stylesheet"><meta name="msapplication-TileColor" content="#000000"><link rel="apple-touch-icon" sizes="180x180" href="https://jetbrains.com/apple-touch-icon.png"><link rel="icon" type="image/png" sizes="32x32" href="https://jetbrains.com/favicon-32x32.png"><link rel="icon" type="image/png" sizes="16x16" href="https://jetbrains.com/favicon-16x16.png"><meta name="msapplication-TileImage" content="https://resources.jetbrains.com/storage/ui/favicons/mstile-144x144.png"><meta name="msapplication-square70x70logo" content="https://resources.jetbrains.com/storage/ui/favicons/mstile-70x70.png"><meta name="msapplication-square150x150logo" content="https://resources.jetbrains.com/storage/ui/favicons/mstile-150x150.png"><meta name="msapplication-wide310x150logo" content="https://resources.jetbrains.com/storage/ui/favicons/mstile-310x150.png"><meta name="msapplication-square310x310logo" content="https://resources.jetbrains.com/storage/ui/favicons/mstile-310x310.png"><meta name="image" content=""><!-- Open Graph --><meta property="og:title" content="List voice calls | Copenhagen Truck Wash API"><meta property="og:description" content=""><meta property="og:image" content=""><meta property="og:site_name" content="Copenhagen Truck Wash API Help"><meta property="og:type" content="website"><meta property="og:locale" content="en_US"><meta property="og:url" content="writerside-documentation/birdlistvoicecalls.html"><!-- End Open Graph --><!-- Twitter Card --><meta name="twitter:card" content="summary_large_image"><meta name="twitter:site" content=""><meta name="twitter:title" content="List voice calls | Copenhagen Truck Wash API"><meta name="twitter:description" content=""><meta name="twitter:creator" content=""><meta name="twitter:image:src" content=""><!-- End Twitter Card --><!-- Schema.org WebPage --><script type="application/ld+json">{
"@context": "http://schema.org",
"@type": "WebPage",
"@id": "writerside-documentation/birdlistvoicecalls.html#webpage",
"url": "writerside-documentation/birdlistvoicecalls.html",
"name": "List voice calls | Copenhagen Truck Wash API",
"description": "",
"image": "",
"inLanguage":"en-US"
}</script><!-- End Schema.org --><!-- Schema.org WebSite --><script type="application/ld+json">{
"@type": "WebSite",
"@id": "writerside-documentation/#website",
"url": "writerside-documentation/",
"name": "Copenhagen Truck Wash API Help"
}</script><!-- End Schema.org --></head><body data-id="birdListVoiceCalls" data-main-title="List voice calls" data-article-props="{&quot;seeAlsoStyle&quot;:&quot;links&quot;}" data-template="article" data-breadcrumbs="API-Reference.topic|API Reference///Tag_Bird.topic|Bird///Tag_Bird_Page_1.topic|Bird - Page 1 of 1"><div class="wrapper"><main class="panel _main"><header class="panel__header"><div class="container"><h3>Copenhagen Truck Wash API Help</h3><div class="panel-trigger"></div></div></header><section class="panel__content"><div class="container"><article class="article" data-shortcut-switcher="inactive"><h1 data-toc="birdListVoiceCalls" id="birdListVoiceCalls.topic">List voice calls</h1><p id="t2bna2_2">This endpoint documentation is generated directly from <code class="code" id="t2bna2_8">openapi.yaml</code>.</p><section class="chapter"><h2 id="endpoint" data-toc="endpoint">Endpoint</h2><div class="code-block" data-lang="http">GET /bird/voice/calls</div></section><section class="chapter"><h2 id="operation" data-toc="operation">Operation</h2><p id="t2bna2_10">Operation ID: <code class="code" id="t2bna2_12">birdListVoiceCalls</code></p><p id="t2bna2_11">List voice calls</p></section><section class="chapter"><h2 id="authentication" data-toc="authentication">Authentication</h2><p id="t2bna2_13">Security requirements:</p><div class="table-wrapper"><table class="wide" id="t2bna2_14"><thead><tr class="ijRowHead" id="t2bna2_15"><th id="t2bna2_17"><p>Scheme</p></th><th id="t2bna2_18"><p>Scopes</p></th></tr></thead><tbody><tr id="t2bna2_16"><td id="t2bna2_19"><p>BearerAuth</p></td><td id="t2bna2_20"><p>-</p></td></tr></tbody></table></div></section><section class="chapter"><h2 id="parameters" data-toc="parameters">Parameters</h2><div class="table-wrapper"><table class="wide" id="t2bna2_21"><thead><tr class="ijRowHead" id="t2bna2_22"><th id="t2bna2_26"><p>Name</p></th><th id="t2bna2_27"><p>In</p></th><th id="t2bna2_28"><p>Required</p></th><th id="t2bna2_29"><p>Type</p></th><th id="t2bna2_30"><p>Description</p></th></tr></thead><tbody><tr id="t2bna2_23"><td id="t2bna2_31"><p>workspaceId</p></td><td id="t2bna2_32"><p>query</p></td><td id="t2bna2_33"><p>no</p></td><td id="t2bna2_34"><p>string</p></td><td id="t2bna2_35"><p>Bird Workspace identifier (falls back to module configuration if omitted)</p></td></tr><tr id="t2bna2_24"><td id="t2bna2_36"><p>channelId</p></td><td id="t2bna2_37"><p>query</p></td><td id="t2bna2_38"><p>no</p></td><td id="t2bna2_39"><p>string</p></td><td id="t2bna2_40"><p>Bird Channel identifier (falls back to module configuration if omitted)</p></td></tr><tr id="t2bna2_25"><td id="t2bna2_41"><p>page</p></td><td id="t2bna2_42"><p>query</p></td><td id="t2bna2_43"><p>no</p></td><td id="t2bna2_44"><p>integer</p></td><td id="t2bna2_45"></td></tr></tbody></table></div></section><section class="chapter"><h2 id="responses" data-toc="responses">Responses</h2><div class="table-wrapper"><table class="wide" id="t2bna2_46"><thead><tr class="ijRowHead" id="t2bna2_49"><th id="t2bna2_51"><p>Status</p></th><th id="t2bna2_52"><p>Description</p></th><th id="t2bna2_53"><p>Content Types</p></th></tr></thead><tbody><tr id="t2bna2_50"><td id="t2bna2_54"><p>200</p></td><td id="t2bna2_55"><p>A list of calls</p></td><td id="t2bna2_56"><p>application/json</p></td></tr></tbody></table></div><p id="t2bna2_47">Schema for response <code class="code" id="t2bna2_57">200</code> (<code class="code" id="t2bna2_58">application/json</code>):</p><div class="code-block" data-lang="json">
{
&quot;$ref&quot;: &quot;#/components/schemas/BirdVoiceCallListResponse&quot;
}
</div></section><div class="last-modified">17 March 2026</div><div data-feedback-placeholder="true"></div><div class="navigation-links _bottom"><a href="birdgetnumber.html" class="navigation-links__prev">Get a number by ID</a><a href="birdcreatevoicecall.html" class="navigation-links__next">Create/place a voice call via Bird</a></div></article><div id="disqus_thread"></div></div></section></main></div><script src="https://resources.jetbrains.com/writerside/apidoc/6.26.0-b913/app.js"></script></body></html>
@@ -0,0 +1,59 @@
<!DOCTYPE html SYSTEM "about:legacy-compat">
<html lang="en-US" data-preset="contrast" data-primary-color="#307FFF"><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8"><meta charset="UTF-8"><meta name="robots" content="noindex"><meta name="built-on" content="2026-03-17T15:43:30.379602855"><title>Say a message on an active voice call and hang up afterwards | Copenhagen Truck Wash API</title><script type="application/json" id="virtual-toc-data">[{"id":"endpoint","level":0,"title":"Endpoint","anchor":"#endpoint"},{"id":"operation","level":0,"title":"Operation","anchor":"#operation"},{"id":"authentication","level":0,"title":"Authentication","anchor":"#authentication"},{"id":"parameters","level":0,"title":"Parameters","anchor":"#parameters"},{"id":"request-body","level":0,"title":"Request Body","anchor":"#request-body"},{"id":"responses","level":0,"title":"Responses","anchor":"#responses"}]</script><script type="application/json" id="topic-shortcuts"></script><link href="https://resources.jetbrains.com/writerside/apidoc/6.26.0-b913/app.css" rel="stylesheet"><meta name="msapplication-TileColor" content="#000000"><link rel="apple-touch-icon" sizes="180x180" href="https://jetbrains.com/apple-touch-icon.png"><link rel="icon" type="image/png" sizes="32x32" href="https://jetbrains.com/favicon-32x32.png"><link rel="icon" type="image/png" sizes="16x16" href="https://jetbrains.com/favicon-16x16.png"><meta name="msapplication-TileImage" content="https://resources.jetbrains.com/storage/ui/favicons/mstile-144x144.png"><meta name="msapplication-square70x70logo" content="https://resources.jetbrains.com/storage/ui/favicons/mstile-70x70.png"><meta name="msapplication-square150x150logo" content="https://resources.jetbrains.com/storage/ui/favicons/mstile-150x150.png"><meta name="msapplication-wide310x150logo" content="https://resources.jetbrains.com/storage/ui/favicons/mstile-310x150.png"><meta name="msapplication-square310x310logo" content="https://resources.jetbrains.com/storage/ui/favicons/mstile-310x310.png"><meta name="image" content=""><!-- Open Graph --><meta property="og:title" content="Say a message on an active voice call and hang up afterwards | Copenhagen Truck Wash API"><meta property="og:description" content=""><meta property="og:image" content=""><meta property="og:site_name" content="Copenhagen Truck Wash API Help"><meta property="og:type" content="website"><meta property="og:locale" content="en_US"><meta property="og:url" content="writerside-documentation/birdsayonvoicecall.html"><!-- End Open Graph --><!-- Twitter Card --><meta name="twitter:card" content="summary_large_image"><meta name="twitter:site" content=""><meta name="twitter:title" content="Say a message on an active voice call and hang up afterwards | Copenhagen Truck Wash API"><meta name="twitter:description" content=""><meta name="twitter:creator" content=""><meta name="twitter:image:src" content=""><!-- End Twitter Card --><!-- Schema.org WebPage --><script type="application/ld+json">{
"@context": "http://schema.org",
"@type": "WebPage",
"@id": "writerside-documentation/birdsayonvoicecall.html#webpage",
"url": "writerside-documentation/birdsayonvoicecall.html",
"name": "Say a message on an active voice call and hang up afterwards | Copenhagen Truck Wash API",
"description": "",
"image": "",
"inLanguage":"en-US"
}</script><!-- End Schema.org --><!-- Schema.org WebSite --><script type="application/ld+json">{
"@type": "WebSite",
"@id": "writerside-documentation/#website",
"url": "writerside-documentation/",
"name": "Copenhagen Truck Wash API Help"
}</script><!-- End Schema.org --></head><body data-id="birdSayOnVoiceCall" data-main-title="Say a message on an active voice call and hang up afterwards" data-article-props="{&quot;seeAlsoStyle&quot;:&quot;links&quot;}" data-template="article" data-breadcrumbs="API-Reference.topic|API Reference///Tag_Bird.topic|Bird///Tag_Bird_Page_1.topic|Bird - Page 1 of 1"><div class="wrapper"><main class="panel _main"><header class="panel__header"><div class="container"><h3>Copenhagen Truck Wash API Help</h3><div class="panel-trigger"></div></div></header><section class="panel__content"><div class="container"><article class="article" data-shortcut-switcher="inactive"><h1 data-toc="birdSayOnVoiceCall" id="birdSayOnVoiceCall.topic">Say a message on an active voice call and hang up afterwards</h1><p id="-evlgu9_2">This endpoint documentation is generated directly from <code class="code" id="-evlgu9_9">openapi.yaml</code>.</p><section class="chapter"><h2 id="endpoint" data-toc="endpoint">Endpoint</h2><div class="code-block" data-lang="http">POST /bird/voice/calls/{id}/say</div></section><section class="chapter"><h2 id="operation" data-toc="operation">Operation</h2><p id="-evlgu9_11">Operation ID: <code class="code" id="-evlgu9_13">birdSayOnVoiceCall</code></p><p id="-evlgu9_12">Say a message on an active voice call and hang up afterwards</p></section><section class="chapter"><h2 id="authentication" data-toc="authentication">Authentication</h2><p id="-evlgu9_14">Security requirements:</p><div class="table-wrapper"><table class="wide" id="-evlgu9_15"><thead><tr class="ijRowHead" id="-evlgu9_16"><th id="-evlgu9_18"><p>Scheme</p></th><th id="-evlgu9_19"><p>Scopes</p></th></tr></thead><tbody><tr id="-evlgu9_17"><td id="-evlgu9_20"><p>BearerAuth</p></td><td id="-evlgu9_21"><p>-</p></td></tr></tbody></table></div></section><section class="chapter"><h2 id="parameters" data-toc="parameters">Parameters</h2><div class="table-wrapper"><table class="wide" id="-evlgu9_22"><thead><tr class="ijRowHead" id="-evlgu9_23"><th id="-evlgu9_27"><p>Name</p></th><th id="-evlgu9_28"><p>In</p></th><th id="-evlgu9_29"><p>Required</p></th><th id="-evlgu9_30"><p>Type</p></th><th id="-evlgu9_31"><p>Description</p></th></tr></thead><tbody><tr id="-evlgu9_24"><td id="-evlgu9_32"><p>workspaceId</p></td><td id="-evlgu9_33"><p>query</p></td><td id="-evlgu9_34"><p>no</p></td><td id="-evlgu9_35"><p>string</p></td><td id="-evlgu9_36"><p>Bird Workspace identifier (falls back to module configuration if omitted)</p></td></tr><tr id="-evlgu9_25"><td id="-evlgu9_37"><p>channelId</p></td><td id="-evlgu9_38"><p>query</p></td><td id="-evlgu9_39"><p>no</p></td><td id="-evlgu9_40"><p>string</p></td><td id="-evlgu9_41"><p>Bird Channel identifier (falls back to module configuration if omitted)</p></td></tr><tr id="-evlgu9_26"><td id="-evlgu9_42"><p>id</p></td><td id="-evlgu9_43"><p>path</p></td><td id="-evlgu9_44"><p>yes</p></td><td id="-evlgu9_45"><p>string</p></td><td id="-evlgu9_46"><p>Call identifier</p></td></tr></tbody></table></div></section><section class="chapter"><h2 id="request-body" data-toc="request-body">Request Body</h2><p id="-evlgu9_47">Required: yes.</p><p id="-evlgu9_48">Content type: <code class="code" id="-evlgu9_50">application/json</code></p><div class="code-block" data-lang="json">
{
&quot;properties&quot;: {
&quot;hangup&quot;: {
&quot;description&quot;: &quot;Whether to hang up the call after the message finishes playing (defaults to true)&quot;,
&quot;example&quot;: true,
&quot;type&quot;: &quot;boolean&quot;
},
&quot;locale&quot;: {
&quot;description&quot;: &quot;The locale to use for the TTS voice (e.g. en-US)&quot;,
&quot;example&quot;: &quot;en-US&quot;,
&quot;type&quot;: &quot;string&quot;
},
&quot;loop&quot;: {
&quot;description&quot;: &quot;Number of times to loop the message&quot;,
&quot;example&quot;: 1,
&quot;type&quot;: &quot;integer&quot;
},
&quot;text&quot;: {
&quot;description&quot;: &quot;The text message to play via TTS&quot;,
&quot;example&quot;: &quot;The gate will open shortly.&quot;,
&quot;type&quot;: &quot;string&quot;
},
&quot;timeout&quot;: {
&quot;description&quot;: &quot;Timeout in seconds for the TTS action&quot;,
&quot;example&quot;: 1,
&quot;type&quot;: &quot;integer&quot;
},
&quot;voice&quot;: {
&quot;description&quot;: &quot;The voice identifier to use&quot;,
&quot;example&quot;: &quot;male&quot;,
&quot;type&quot;: &quot;string&quot;
}
},
&quot;required&quot;: [
&quot;text&quot;
],
&quot;type&quot;: &quot;object&quot;
}
</div></section><section class="chapter"><h2 id="responses" data-toc="responses">Responses</h2><div class="table-wrapper"><table class="wide" id="-evlgu9_51"><thead><tr class="ijRowHead" id="-evlgu9_54"><th id="-evlgu9_56"><p>Status</p></th><th id="-evlgu9_57"><p>Description</p></th><th id="-evlgu9_58"><p>Content Types</p></th></tr></thead><tbody><tr id="-evlgu9_55"><td id="-evlgu9_59"><p>200</p></td><td id="-evlgu9_60"><p>TTS action requested</p></td><td id="-evlgu9_61"><p>application/json</p></td></tr></tbody></table></div><p id="-evlgu9_52">Schema for response <code class="code" id="-evlgu9_62">200</code> (<code class="code" id="-evlgu9_63">application/json</code>):</p><div class="code-block" data-lang="json">
{
&quot;$ref&quot;: &quot;#/components/schemas/BirdVoiceCallSingleResponse&quot;
}
</div></section><div class="last-modified">17 March 2026</div><div data-feedback-placeholder="true"></div><div class="navigation-links _bottom"><a href="birdhangupvoicecall.html" class="navigation-links__prev">Hang up a voice call by ID</a><a href="birdlistflashcalls.html" class="navigation-links__next">List flash calls</a></div></article><div id="disqus_thread"></div></div></section></main></div><script src="https://resources.jetbrains.com/writerside/apidoc/6.26.0-b913/app.js"></script></body></html>
@@ -0,0 +1,48 @@
<!DOCTYPE html SYSTEM "about:legacy-compat">
<html lang="en-US" data-preset="contrast" data-primary-color="#307FFF"><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8"><meta charset="UTF-8"><meta name="robots" content="noindex"><meta name="built-on" content="2026-03-17T15:42:14.677040207"><title>Place a test outbound call and hang up when accepted | Copenhagen Truck Wash API</title><script type="application/json" id="virtual-toc-data">[{"id":"endpoint","level":0,"title":"Endpoint","anchor":"#endpoint"},{"id":"operation","level":0,"title":"Operation","anchor":"#operation"},{"id":"authentication","level":0,"title":"Authentication","anchor":"#authentication"},{"id":"parameters","level":0,"title":"Parameters","anchor":"#parameters"},{"id":"request-body","level":0,"title":"Request Body","anchor":"#request-body"},{"id":"responses","level":0,"title":"Responses","anchor":"#responses"}]</script><script type="application/json" id="topic-shortcuts"></script><link href="https://resources.jetbrains.com/writerside/apidoc/6.26.0-b913/app.css" rel="stylesheet"><meta name="msapplication-TileColor" content="#000000"><link rel="apple-touch-icon" sizes="180x180" href="https://jetbrains.com/apple-touch-icon.png"><link rel="icon" type="image/png" sizes="32x32" href="https://jetbrains.com/favicon-32x32.png"><link rel="icon" type="image/png" sizes="16x16" href="https://jetbrains.com/favicon-16x16.png"><meta name="msapplication-TileImage" content="https://resources.jetbrains.com/storage/ui/favicons/mstile-144x144.png"><meta name="msapplication-square70x70logo" content="https://resources.jetbrains.com/storage/ui/favicons/mstile-70x70.png"><meta name="msapplication-square150x150logo" content="https://resources.jetbrains.com/storage/ui/favicons/mstile-150x150.png"><meta name="msapplication-wide310x150logo" content="https://resources.jetbrains.com/storage/ui/favicons/mstile-310x150.png"><meta name="msapplication-square310x310logo" content="https://resources.jetbrains.com/storage/ui/favicons/mstile-310x310.png"><meta name="image" content=""><!-- Open Graph --><meta property="og:title" content="Place a test outbound call and hang up when accepted | Copenhagen Truck Wash API"><meta property="og:description" content=""><meta property="og:image" content=""><meta property="og:site_name" content="Copenhagen Truck Wash API Help"><meta property="og:type" content="website"><meta property="og:locale" content="en_US"><meta property="og:url" content="writerside-documentation/birdtestoutboundvoicecall.html"><!-- End Open Graph --><!-- Twitter Card --><meta name="twitter:card" content="summary_large_image"><meta name="twitter:site" content=""><meta name="twitter:title" content="Place a test outbound call and hang up when accepted | Copenhagen Truck Wash API"><meta name="twitter:description" content=""><meta name="twitter:creator" content=""><meta name="twitter:image:src" content=""><!-- End Twitter Card --><!-- Schema.org WebPage --><script type="application/ld+json">{
"@context": "http://schema.org",
"@type": "WebPage",
"@id": "writerside-documentation/birdtestoutboundvoicecall.html#webpage",
"url": "writerside-documentation/birdtestoutboundvoicecall.html",
"name": "Place a test outbound call and hang up when accepted | Copenhagen Truck Wash API",
"description": "",
"image": "",
"inLanguage":"en-US"
}</script><!-- End Schema.org --><!-- Schema.org WebSite --><script type="application/ld+json">{
"@type": "WebSite",
"@id": "writerside-documentation/#website",
"url": "writerside-documentation/",
"name": "Copenhagen Truck Wash API Help"
}</script><!-- End Schema.org --></head><body data-id="birdTestOutboundVoiceCall" data-main-title="Place a test outbound call and hang up when accepted" data-article-props="{&quot;seeAlsoStyle&quot;:&quot;links&quot;}" data-template="article" data-breadcrumbs="API-Reference.topic|API Reference///Tag_Bird.topic|Bird///Tag_Bird_Page_1.topic|Bird - Page 1 of 1"><div class="wrapper"><main class="panel _main"><header class="panel__header"><div class="container"><h3>Copenhagen Truck Wash API Help</h3><div class="panel-trigger"></div></div></header><section class="panel__content"><div class="container"><article class="article" data-shortcut-switcher="inactive"><h1 data-toc="birdTestOutboundVoiceCall" id="birdTestOutboundVoiceCall.topic">Place a test outbound call and hang up when accepted</h1><p id="-3ucdl_2">This endpoint documentation is generated directly from <code class="code" id="-3ucdl_9">openapi.yaml</code>.</p><section class="chapter"><h2 id="endpoint" data-toc="endpoint">Endpoint</h2><div class="code-block" data-lang="http">POST /bird/voice/calls/test-outbound</div></section><section class="chapter"><h2 id="operation" data-toc="operation">Operation</h2><p id="-3ucdl_11">Operation ID: <code class="code" id="-3ucdl_13">birdTestOutboundVoiceCall</code></p><p id="-3ucdl_12">Calls +45 42 33 11 28 and hangs up when the call reaches accepted/ongoing state.</p></section><section class="chapter"><h2 id="authentication" data-toc="authentication">Authentication</h2><p id="-3ucdl_14">Security requirements:</p><div class="table-wrapper"><table class="wide" id="-3ucdl_15"><thead><tr class="ijRowHead" id="-3ucdl_16"><th id="-3ucdl_18"><p>Scheme</p></th><th id="-3ucdl_19"><p>Scopes</p></th></tr></thead><tbody><tr id="-3ucdl_17"><td id="-3ucdl_20"><p>BearerAuth</p></td><td id="-3ucdl_21"><p>-</p></td></tr></tbody></table></div></section><section class="chapter"><h2 id="parameters" data-toc="parameters">Parameters</h2><div class="table-wrapper"><table class="wide" id="-3ucdl_22"><thead><tr class="ijRowHead" id="-3ucdl_23"><th id="-3ucdl_26"><p>Name</p></th><th id="-3ucdl_27"><p>In</p></th><th id="-3ucdl_28"><p>Required</p></th><th id="-3ucdl_29"><p>Type</p></th><th id="-3ucdl_30"><p>Description</p></th></tr></thead><tbody><tr id="-3ucdl_24"><td id="-3ucdl_31"><p>workspaceId</p></td><td id="-3ucdl_32"><p>query</p></td><td id="-3ucdl_33"><p>no</p></td><td id="-3ucdl_34"><p>string</p></td><td id="-3ucdl_35"><p>Bird Workspace identifier (falls back to module configuration if omitted)</p></td></tr><tr id="-3ucdl_25"><td id="-3ucdl_36"><p>channelId</p></td><td id="-3ucdl_37"><p>query</p></td><td id="-3ucdl_38"><p>no</p></td><td id="-3ucdl_39"><p>string</p></td><td id="-3ucdl_40"><p>Bird Channel identifier (falls back to module configuration if omitted)</p></td></tr></tbody></table></div></section><section class="chapter"><h2 id="request-body" data-toc="request-body">Request Body</h2><p id="-3ucdl_41">Required: no.</p><p id="-3ucdl_42">Content type: <code class="code" id="-3ucdl_44">application/json</code></p><div class="code-block" data-lang="json">
{
&quot;additionalProperties&quot;: true,
&quot;properties&quot;: {
&quot;from&quot;: {
&quot;description&quot;: &quot;Caller E.164 number to use for the test call&quot;,
&quot;example&quot;: &quot;+4599988877&quot;,
&quot;type&quot;: &quot;string&quot;
},
&quot;hangupCause&quot;: {
&quot;description&quot;: &quot;Optional hangup cause passed through to Bird&quot;,
&quot;type&quot;: &quot;string&quot;
},
&quot;maxPollSeconds&quot;: {
&quot;description&quot;: &quot;Max time to wait before timing out&quot;,
&quot;example&quot;: 30,
&quot;minimum&quot;: 5,
&quot;type&quot;: &quot;integer&quot;
},
&quot;pollIntervalSeconds&quot;: {
&quot;description&quot;: &quot;Poll interval while waiting for accepted status&quot;,
&quot;example&quot;: 2,
&quot;minimum&quot;: 1,
&quot;type&quot;: &quot;integer&quot;
}
},
&quot;type&quot;: &quot;object&quot;
}
</div></section><section class="chapter"><h2 id="responses" data-toc="responses">Responses</h2><div class="table-wrapper"><table class="wide" id="-3ucdl_45"><thead><tr class="ijRowHead" id="-3ucdl_48"><th id="-3ucdl_50"><p>Status</p></th><th id="-3ucdl_51"><p>Description</p></th><th id="-3ucdl_52"><p>Content Types</p></th></tr></thead><tbody><tr id="-3ucdl_49"><td id="-3ucdl_53"><p>200</p></td><td id="-3ucdl_54"><p>Test call created and either hung up or timed out</p></td><td id="-3ucdl_55"><p>application/json</p></td></tr></tbody></table></div><p id="-3ucdl_46">Schema for response <code class="code" id="-3ucdl_56">200</code> (<code class="code" id="-3ucdl_57">application/json</code>):</p><div class="code-block" data-lang="json">
{
&quot;$ref&quot;: &quot;#/components/schemas/BirdTestOutboundCallResponse&quot;
}
</div></section><div class="last-modified">17 March 2026</div><div data-feedback-placeholder="true"></div><div class="navigation-links _bottom"><a href="birdcreatevoicecall.html" class="navigation-links__prev">Create/place a voice call via Bird</a><a href="birdgetvoicecall.html" class="navigation-links__next">Get a voice call by ID</a></div></article><div id="disqus_thread"></div></div></section></main></div><script src="https://resources.jetbrains.com/writerside/apidoc/6.26.0-b913/app.js"></script></body></html>
@@ -0,0 +1,30 @@
<!DOCTYPE html SYSTEM "about:legacy-compat">
<html lang="en-US" data-preset="contrast" data-primary-color="#307FFF"><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8"><meta charset="UTF-8"><meta name="robots" content="noindex"><meta name="built-on" content="2026-03-17T15:45:30.124973156"><title>Capture Stripe payment intent | Copenhagen Truck Wash API</title><script type="application/json" id="virtual-toc-data">[{"id":"endpoint","level":0,"title":"Endpoint","anchor":"#endpoint"},{"id":"operation","level":0,"title":"Operation","anchor":"#operation"},{"id":"authentication","level":0,"title":"Authentication","anchor":"#authentication"},{"id":"request-body","level":0,"title":"Request Body","anchor":"#request-body"},{"id":"responses","level":0,"title":"Responses","anchor":"#responses"}]</script><script type="application/json" id="topic-shortcuts"></script><link href="https://resources.jetbrains.com/writerside/apidoc/6.26.0-b913/app.css" rel="stylesheet"><meta name="msapplication-TileColor" content="#000000"><link rel="apple-touch-icon" sizes="180x180" href="https://jetbrains.com/apple-touch-icon.png"><link rel="icon" type="image/png" sizes="32x32" href="https://jetbrains.com/favicon-32x32.png"><link rel="icon" type="image/png" sizes="16x16" href="https://jetbrains.com/favicon-16x16.png"><meta name="msapplication-TileImage" content="https://resources.jetbrains.com/storage/ui/favicons/mstile-144x144.png"><meta name="msapplication-square70x70logo" content="https://resources.jetbrains.com/storage/ui/favicons/mstile-70x70.png"><meta name="msapplication-square150x150logo" content="https://resources.jetbrains.com/storage/ui/favicons/mstile-150x150.png"><meta name="msapplication-wide310x150logo" content="https://resources.jetbrains.com/storage/ui/favicons/mstile-310x150.png"><meta name="msapplication-square310x310logo" content="https://resources.jetbrains.com/storage/ui/favicons/mstile-310x310.png"><meta name="image" content=""><!-- Open Graph --><meta property="og:title" content="Capture Stripe payment intent | Copenhagen Truck Wash API"><meta property="og:description" content=""><meta property="og:image" content=""><meta property="og:site_name" content="Copenhagen Truck Wash API Help"><meta property="og:type" content="website"><meta property="og:locale" content="en_US"><meta property="og:url" content="writerside-documentation/capturestripepaymentintent.html"><!-- End Open Graph --><!-- Twitter Card --><meta name="twitter:card" content="summary_large_image"><meta name="twitter:site" content=""><meta name="twitter:title" content="Capture Stripe payment intent | Copenhagen Truck Wash API"><meta name="twitter:description" content=""><meta name="twitter:creator" content=""><meta name="twitter:image:src" content=""><!-- End Twitter Card --><!-- Schema.org WebPage --><script type="application/ld+json">{
"@context": "http://schema.org",
"@type": "WebPage",
"@id": "writerside-documentation/capturestripepaymentintent.html#webpage",
"url": "writerside-documentation/capturestripepaymentintent.html",
"name": "Capture Stripe payment intent | Copenhagen Truck Wash API",
"description": "",
"image": "",
"inLanguage":"en-US"
}</script><!-- End Schema.org --><!-- Schema.org WebSite --><script type="application/ld+json">{
"@type": "WebSite",
"@id": "writerside-documentation/#website",
"url": "writerside-documentation/",
"name": "Copenhagen Truck Wash API Help"
}</script><!-- End Schema.org --></head><body data-id="captureStripePaymentIntent" data-main-title="Capture Stripe payment intent" data-article-props="{&quot;seeAlsoStyle&quot;:&quot;links&quot;}" data-template="article" data-breadcrumbs="API-Reference.topic|API Reference///Tag_Orders.topic|Orders///Tag_Orders_Page_1.topic|Orders - Page 1 of 1"><div class="wrapper"><main class="panel _main"><header class="panel__header"><div class="container"><h3>Copenhagen Truck Wash API Help</h3><div class="panel-trigger"></div></div></header><section class="panel__content"><div class="container"><article class="article" data-shortcut-switcher="inactive"><h1 data-toc="captureStripePaymentIntent" id="captureStripePaymentIntent.topic">Capture Stripe payment intent</h1><p id="-rzy0kx_2">This endpoint documentation is generated directly from <code class="code" id="-rzy0kx_8">openapi.yaml</code>.</p><section class="chapter"><h2 id="endpoint" data-toc="endpoint">Endpoint</h2><div class="code-block" data-lang="http">POST /orders/module/stripe/payment_intent/capture</div></section><section class="chapter"><h2 id="operation" data-toc="operation">Operation</h2><p id="-rzy0kx_10">Operation ID: <code class="code" id="-rzy0kx_12">captureStripePaymentIntent</code></p><p id="-rzy0kx_11">Capture Stripe payment intent</p></section><section class="chapter"><h2 id="authentication" data-toc="authentication">Authentication</h2><p id="-rzy0kx_13">Security requirements:</p><div class="table-wrapper"><table class="wide" id="-rzy0kx_14"><thead><tr class="ijRowHead" id="-rzy0kx_15"><th id="-rzy0kx_17"><p>Scheme</p></th><th id="-rzy0kx_18"><p>Scopes</p></th></tr></thead><tbody><tr id="-rzy0kx_16"><td id="-rzy0kx_19"><p>BearerAuth</p></td><td id="-rzy0kx_20"><p>-</p></td></tr></tbody></table></div></section><section class="chapter"><h2 id="request-body" data-toc="request-body">Request Body</h2><p id="-rzy0kx_21">Required: yes.</p><p id="-rzy0kx_22">Content type: <code class="code" id="-rzy0kx_24">application/json</code></p><div class="code-block" data-lang="json">
{
&quot;properties&quot;: {
&quot;id&quot;: {
&quot;type&quot;: &quot;integer&quot;
}
},
&quot;required&quot;: [
&quot;id&quot;
],
&quot;type&quot;: &quot;object&quot;
}
</div></section><section class="chapter"><h2 id="responses" data-toc="responses">Responses</h2><div class="table-wrapper"><table class="wide" id="-rzy0kx_25"><thead><tr class="ijRowHead" id="-rzy0kx_28"><th id="-rzy0kx_30"><p>Status</p></th><th id="-rzy0kx_31"><p>Description</p></th><th id="-rzy0kx_32"><p>Content Types</p></th></tr></thead><tbody><tr id="-rzy0kx_29"><td id="-rzy0kx_33"><p>200</p></td><td id="-rzy0kx_34"><p>Success</p></td><td id="-rzy0kx_35"><p>application/json</p></td></tr></tbody></table></div><p id="-rzy0kx_26">Schema for response <code class="code" id="-rzy0kx_36">200</code> (<code class="code" id="-rzy0kx_37">application/json</code>):</p><div class="code-block" data-lang="json">
{}
</div></section><div class="last-modified">17 March 2026</div><div data-feedback-placeholder="true"></div><div class="navigation-links _bottom"><a href="createstripepaymentintent.html" class="navigation-links__prev">Create Stripe payment intent</a><a href="getuserorder.html" class="navigation-links__next">Get user's specific order</a></div></article><div id="disqus_thread"></div></div></section></main></div><script src="https://resources.jetbrains.com/writerside/apidoc/6.26.0-b913/app.js"></script></body></html>
@@ -0,0 +1,18 @@
<!DOCTYPE html SYSTEM "about:legacy-compat">
<html lang="en-US" data-preset="contrast" data-primary-color="#307FFF"><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8"><meta charset="UTF-8"><meta name="robots" content="noindex"><meta name="built-on" content="2026-03-17T15:41:23.924328492"><title>Check if customer exists in e-conomic | Copenhagen Truck Wash API</title><script type="application/json" id="virtual-toc-data">[{"id":"endpoint","level":0,"title":"Endpoint","anchor":"#endpoint"},{"id":"operation","level":0,"title":"Operation","anchor":"#operation"},{"id":"authentication","level":0,"title":"Authentication","anchor":"#authentication"},{"id":"parameters","level":0,"title":"Parameters","anchor":"#parameters"},{"id":"responses","level":0,"title":"Responses","anchor":"#responses"}]</script><script type="application/json" id="topic-shortcuts"></script><link href="https://resources.jetbrains.com/writerside/apidoc/6.26.0-b913/app.css" rel="stylesheet"><meta name="msapplication-TileColor" content="#000000"><link rel="apple-touch-icon" sizes="180x180" href="https://jetbrains.com/apple-touch-icon.png"><link rel="icon" type="image/png" sizes="32x32" href="https://jetbrains.com/favicon-32x32.png"><link rel="icon" type="image/png" sizes="16x16" href="https://jetbrains.com/favicon-16x16.png"><meta name="msapplication-TileImage" content="https://resources.jetbrains.com/storage/ui/favicons/mstile-144x144.png"><meta name="msapplication-square70x70logo" content="https://resources.jetbrains.com/storage/ui/favicons/mstile-70x70.png"><meta name="msapplication-square150x150logo" content="https://resources.jetbrains.com/storage/ui/favicons/mstile-150x150.png"><meta name="msapplication-wide310x150logo" content="https://resources.jetbrains.com/storage/ui/favicons/mstile-310x150.png"><meta name="msapplication-square310x310logo" content="https://resources.jetbrains.com/storage/ui/favicons/mstile-310x310.png"><meta name="image" content=""><!-- Open Graph --><meta property="og:title" content="Check if customer exists in e-conomic | Copenhagen Truck Wash API"><meta property="og:description" content=""><meta property="og:image" content=""><meta property="og:site_name" content="Copenhagen Truck Wash API Help"><meta property="og:type" content="website"><meta property="og:locale" content="en_US"><meta property="og:url" content="writerside-documentation/checkeconomiccustomerexists.html"><!-- End Open Graph --><!-- Twitter Card --><meta name="twitter:card" content="summary_large_image"><meta name="twitter:site" content=""><meta name="twitter:title" content="Check if customer exists in e-conomic | Copenhagen Truck Wash API"><meta name="twitter:description" content=""><meta name="twitter:creator" content=""><meta name="twitter:image:src" content=""><!-- End Twitter Card --><!-- Schema.org WebPage --><script type="application/ld+json">{
"@context": "http://schema.org",
"@type": "WebPage",
"@id": "writerside-documentation/checkeconomiccustomerexists.html#webpage",
"url": "writerside-documentation/checkeconomiccustomerexists.html",
"name": "Check if customer exists in e-conomic | Copenhagen Truck Wash API",
"description": "",
"image": "",
"inLanguage":"en-US"
}</script><!-- End Schema.org --><!-- Schema.org WebSite --><script type="application/ld+json">{
"@type": "WebSite",
"@id": "writerside-documentation/#website",
"url": "writerside-documentation/",
"name": "Copenhagen Truck Wash API Help"
}</script><!-- End Schema.org --></head><body data-id="checkEconomicCustomerExists" data-main-title="Check if customer exists in e-conomic" data-article-props="{&quot;seeAlsoStyle&quot;:&quot;links&quot;}" data-template="article" data-breadcrumbs="API-Reference.topic|API Reference///Tag_Modules.topic|Modules///modules_module_e_conomic.topic///modules_module_e_conomic_page_1.topic"><div class="wrapper"><main class="panel _main"><header class="panel__header"><div class="container"><h3>Copenhagen Truck Wash API Help</h3><div class="panel-trigger"></div></div></header><section class="panel__content"><div class="container"><article class="article" data-shortcut-switcher="inactive"><h1 data-toc="checkEconomicCustomerExists" id="checkEconomicCustomerExists.topic">Check if customer exists in e-conomic</h1><p id="-q97pmv_2">This endpoint documentation is generated directly from <code class="code" id="-q97pmv_8">openapi.yaml</code>.</p><section class="chapter"><h2 id="endpoint" data-toc="endpoint">Endpoint</h2><div class="code-block" data-lang="http">GET /economic/doesCustomerExist</div></section><section class="chapter"><h2 id="operation" data-toc="operation">Operation</h2><p id="-q97pmv_10">Operation ID: <code class="code" id="-q97pmv_12">checkEconomicCustomerExists</code></p><p id="-q97pmv_11">Check if customer exists in e-conomic</p></section><section class="chapter"><h2 id="authentication" data-toc="authentication">Authentication</h2><p id="-q97pmv_13">Security requirements:</p><div class="table-wrapper"><table class="wide" id="-q97pmv_14"><thead><tr class="ijRowHead" id="-q97pmv_15"><th id="-q97pmv_17"><p>Scheme</p></th><th id="-q97pmv_18"><p>Scopes</p></th></tr></thead><tbody><tr id="-q97pmv_16"><td id="-q97pmv_19"><p>BearerAuth</p></td><td id="-q97pmv_20"><p>-</p></td></tr></tbody></table></div></section><section class="chapter"><h2 id="parameters" data-toc="parameters">Parameters</h2><div class="table-wrapper"><table class="wide" id="-q97pmv_21"><thead><tr class="ijRowHead" id="-q97pmv_22"><th id="-q97pmv_24"><p>Name</p></th><th id="-q97pmv_25"><p>In</p></th><th id="-q97pmv_26"><p>Required</p></th><th id="-q97pmv_27"><p>Type</p></th><th id="-q97pmv_28"><p>Description</p></th></tr></thead><tbody><tr id="-q97pmv_23"><td id="-q97pmv_29"><p>cvr</p></td><td id="-q97pmv_30"><p>query</p></td><td id="-q97pmv_31"><p>yes</p></td><td id="-q97pmv_32"><p>string</p></td><td id="-q97pmv_33"></td></tr></tbody></table></div></section><section class="chapter"><h2 id="responses" data-toc="responses">Responses</h2><div class="table-wrapper"><table class="wide" id="-q97pmv_34"><thead><tr class="ijRowHead" id="-q97pmv_37"><th id="-q97pmv_40"><p>Status</p></th><th id="-q97pmv_41"><p>Description</p></th><th id="-q97pmv_42"><p>Content Types</p></th></tr></thead><tbody><tr id="-q97pmv_38"><td id="-q97pmv_43"><p>200</p></td><td id="-q97pmv_44"><p>Customer check completed</p></td><td id="-q97pmv_45"><p>application/json</p></td></tr><tr id="-q97pmv_39"><td id="-q97pmv_46"><p>404</p></td><td id="-q97pmv_47"></td><td id="-q97pmv_48"></td></tr></tbody></table></div><p id="-q97pmv_35">Schema for response <code class="code" id="-q97pmv_49">200</code> (<code class="code" id="-q97pmv_50">application/json</code>):</p><div class="code-block" data-lang="json">
{}
</div></section><div class="last-modified">17 March 2026</div><div data-feedback-placeholder="true"></div><div class="navigation-links _bottom"><a href="searchcvr.html" class="navigation-links__prev">Search CVR</a><a href="createeconomiccustomer.html" class="navigation-links__next">Create e-conomic customer</a></div></article><div id="disqus_thread"></div></div></section></main></div><script src="https://resources.jetbrains.com/writerside/apidoc/6.26.0-b913/app.js"></script></body></html>
@@ -0,0 +1,20 @@
<!DOCTYPE html SYSTEM "about:legacy-compat">
<html lang="en-US" data-preset="contrast" data-primary-color="#307FFF"><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8"><meta charset="UTF-8"><meta name="robots" content="noindex"><meta name="built-on" content="2026-03-17T15:40:13.488619106"><title>Clear system search caches | Copenhagen Truck Wash API</title><script type="application/json" id="virtual-toc-data">[{"id":"endpoint","level":0,"title":"Endpoint","anchor":"#endpoint"},{"id":"operation","level":0,"title":"Operation","anchor":"#operation"},{"id":"authentication","level":0,"title":"Authentication","anchor":"#authentication"},{"id":"responses","level":0,"title":"Responses","anchor":"#responses"}]</script><script type="application/json" id="topic-shortcuts"></script><link href="https://resources.jetbrains.com/writerside/apidoc/6.26.0-b913/app.css" rel="stylesheet"><meta name="msapplication-TileColor" content="#000000"><link rel="apple-touch-icon" sizes="180x180" href="https://jetbrains.com/apple-touch-icon.png"><link rel="icon" type="image/png" sizes="32x32" href="https://jetbrains.com/favicon-32x32.png"><link rel="icon" type="image/png" sizes="16x16" href="https://jetbrains.com/favicon-16x16.png"><meta name="msapplication-TileImage" content="https://resources.jetbrains.com/storage/ui/favicons/mstile-144x144.png"><meta name="msapplication-square70x70logo" content="https://resources.jetbrains.com/storage/ui/favicons/mstile-70x70.png"><meta name="msapplication-square150x150logo" content="https://resources.jetbrains.com/storage/ui/favicons/mstile-150x150.png"><meta name="msapplication-wide310x150logo" content="https://resources.jetbrains.com/storage/ui/favicons/mstile-310x150.png"><meta name="msapplication-square310x310logo" content="https://resources.jetbrains.com/storage/ui/favicons/mstile-310x310.png"><meta name="image" content=""><!-- Open Graph --><meta property="og:title" content="Clear system search caches | Copenhagen Truck Wash API"><meta property="og:description" content=""><meta property="og:image" content=""><meta property="og:site_name" content="Copenhagen Truck Wash API Help"><meta property="og:type" content="website"><meta property="og:locale" content="en_US"><meta property="og:url" content="writerside-documentation/clearsystemsearchcache.html"><!-- End Open Graph --><!-- Twitter Card --><meta name="twitter:card" content="summary_large_image"><meta name="twitter:site" content=""><meta name="twitter:title" content="Clear system search caches | Copenhagen Truck Wash API"><meta name="twitter:description" content=""><meta name="twitter:creator" content=""><meta name="twitter:image:src" content=""><!-- End Twitter Card --><!-- Schema.org WebPage --><script type="application/ld+json">{
"@context": "http://schema.org",
"@type": "WebPage",
"@id": "writerside-documentation/clearsystemsearchcache.html#webpage",
"url": "writerside-documentation/clearsystemsearchcache.html",
"name": "Clear system search caches | Copenhagen Truck Wash API",
"description": "",
"image": "",
"inLanguage":"en-US"
}</script><!-- End Schema.org --><!-- Schema.org WebSite --><script type="application/ld+json">{
"@type": "WebSite",
"@id": "writerside-documentation/#website",
"url": "writerside-documentation/",
"name": "Copenhagen Truck Wash API Help"
}</script><!-- End Schema.org --></head><body data-id="clearSystemSearchCache" data-main-title="Clear system search caches" data-article-props="{&quot;seeAlsoStyle&quot;:&quot;links&quot;}" data-template="article" data-breadcrumbs="API-Reference.topic|API Reference///Tag_Search.topic|Search///Tag_Search_Page_1.topic|Search - Page 1 of 1"><div class="wrapper"><main class="panel _main"><header class="panel__header"><div class="container"><h3>Copenhagen Truck Wash API Help</h3><div class="panel-trigger"></div></div></header><section class="panel__content"><div class="container"><article class="article" data-shortcut-switcher="inactive"><h1 data-toc="clearSystemSearchCache" id="clearSystemSearchCache.topic">Clear system search caches</h1><p id="zddv7i_2">This endpoint documentation is generated directly from <code class="code" id="zddv7i_7">openapi.yaml</code>.</p><section class="chapter"><h2 id="endpoint" data-toc="endpoint">Endpoint</h2><div class="code-block" data-lang="http">DELETE /superuser/search/system/cache</div></section><section class="chapter"><h2 id="operation" data-toc="operation">Operation</h2><p id="zddv7i_9">Operation ID: <code class="code" id="zddv7i_11">clearSystemSearchCache</code></p><p id="zddv7i_10">Clears both query-result cache and intent-parser cache namespaces for system-wide search.</p></section><section class="chapter"><h2 id="authentication" data-toc="authentication">Authentication</h2><p id="zddv7i_12">Security requirements:</p><div class="table-wrapper"><table class="wide" id="zddv7i_13"><thead><tr class="ijRowHead" id="zddv7i_14"><th id="zddv7i_16"><p>Scheme</p></th><th id="zddv7i_17"><p>Scopes</p></th></tr></thead><tbody><tr id="zddv7i_15"><td id="zddv7i_18"><p>BearerAuth</p></td><td id="zddv7i_19"><p>-</p></td></tr></tbody></table></div></section><section class="chapter"><h2 id="responses" data-toc="responses">Responses</h2><div class="table-wrapper"><table class="wide" id="zddv7i_20"><thead><tr class="ijRowHead" id="zddv7i_23"><th id="zddv7i_27"><p>Status</p></th><th id="zddv7i_28"><p>Description</p></th><th id="zddv7i_29"><p>Content Types</p></th></tr></thead><tbody><tr id="zddv7i_24"><td id="zddv7i_30"><p>200</p></td><td id="zddv7i_31"><p>Cache cleared successfully</p></td><td id="zddv7i_32"><p>application/json</p></td></tr><tr id="zddv7i_25"><td id="zddv7i_33"><p>401</p></td><td id="zddv7i_34"></td><td id="zddv7i_35"></td></tr><tr id="zddv7i_26"><td id="zddv7i_36"><p>403</p></td><td id="zddv7i_37"></td><td id="zddv7i_38"></td></tr></tbody></table></div><p id="zddv7i_21">Schema for response <code class="code" id="zddv7i_39">200</code> (<code class="code" id="zddv7i_40">application/json</code>):</p><div class="code-block" data-lang="json">
{
&quot;$ref&quot;: &quot;#/components/schemas/SystemSearchCacheClearResponse&quot;
}
</div></section><div class="last-modified">17 March 2026</div><div data-feedback-placeholder="true"></div><div class="navigation-links _bottom"><a href="systemwidesearchpost.html" class="navigation-links__prev">System-wide search</a><a href="rebuildsystemsearchcache.html" class="navigation-links__next">Queue system search cache rebuild</a></div></article><div id="disqus_thread"></div></div></section></main></div><script src="https://resources.jetbrains.com/writerside/apidoc/6.26.0-b913/app.js"></script></body></html>
@@ -0,0 +1,34 @@
<!DOCTYPE html SYSTEM "about:legacy-compat">
<html lang="en-US" data-preset="contrast" data-primary-color="#307FFF"><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8"><meta charset="UTF-8"><meta name="robots" content="noindex"><meta name="built-on" content="2026-03-17T15:41:27.438662166"><title>Clone role | Copenhagen Truck Wash API</title><script type="application/json" id="virtual-toc-data">[{"id":"endpoint","level":0,"title":"Endpoint","anchor":"#endpoint"},{"id":"operation","level":0,"title":"Operation","anchor":"#operation"},{"id":"authentication","level":0,"title":"Authentication","anchor":"#authentication"},{"id":"request-body","level":0,"title":"Request Body","anchor":"#request-body"},{"id":"responses","level":0,"title":"Responses","anchor":"#responses"}]</script><script type="application/json" id="topic-shortcuts"></script><link href="https://resources.jetbrains.com/writerside/apidoc/6.26.0-b913/app.css" rel="stylesheet"><meta name="msapplication-TileColor" content="#000000"><link rel="apple-touch-icon" sizes="180x180" href="https://jetbrains.com/apple-touch-icon.png"><link rel="icon" type="image/png" sizes="32x32" href="https://jetbrains.com/favicon-32x32.png"><link rel="icon" type="image/png" sizes="16x16" href="https://jetbrains.com/favicon-16x16.png"><meta name="msapplication-TileImage" content="https://resources.jetbrains.com/storage/ui/favicons/mstile-144x144.png"><meta name="msapplication-square70x70logo" content="https://resources.jetbrains.com/storage/ui/favicons/mstile-70x70.png"><meta name="msapplication-square150x150logo" content="https://resources.jetbrains.com/storage/ui/favicons/mstile-150x150.png"><meta name="msapplication-wide310x150logo" content="https://resources.jetbrains.com/storage/ui/favicons/mstile-310x150.png"><meta name="msapplication-square310x310logo" content="https://resources.jetbrains.com/storage/ui/favicons/mstile-310x310.png"><meta name="image" content=""><!-- Open Graph --><meta property="og:title" content="Clone role | Copenhagen Truck Wash API"><meta property="og:description" content=""><meta property="og:image" content=""><meta property="og:site_name" content="Copenhagen Truck Wash API Help"><meta property="og:type" content="website"><meta property="og:locale" content="en_US"><meta property="og:url" content="writerside-documentation/clonerole.html"><!-- End Open Graph --><!-- Twitter Card --><meta name="twitter:card" content="summary_large_image"><meta name="twitter:site" content=""><meta name="twitter:title" content="Clone role | Copenhagen Truck Wash API"><meta name="twitter:description" content=""><meta name="twitter:creator" content=""><meta name="twitter:image:src" content=""><!-- End Twitter Card --><!-- Schema.org WebPage --><script type="application/ld+json">{
"@context": "http://schema.org",
"@type": "WebPage",
"@id": "writerside-documentation/clonerole.html#webpage",
"url": "writerside-documentation/clonerole.html",
"name": "Clone role | Copenhagen Truck Wash API",
"description": "",
"image": "",
"inLanguage":"en-US"
}</script><!-- End Schema.org --><!-- Schema.org WebSite --><script type="application/ld+json">{
"@type": "WebSite",
"@id": "writerside-documentation/#website",
"url": "writerside-documentation/",
"name": "Copenhagen Truck Wash API Help"
}</script><!-- End Schema.org --></head><body data-id="cloneRole" data-main-title="Clone role" data-article-props="{&quot;seeAlsoStyle&quot;:&quot;links&quot;}" data-template="article" data-breadcrumbs="API-Reference.topic|API Reference///Tag_Roles.topic|Roles///Tag_Roles_Page_1.topic|Roles - Page 1 of 1"><div class="wrapper"><main class="panel _main"><header class="panel__header"><div class="container"><h3>Copenhagen Truck Wash API Help</h3><div class="panel-trigger"></div></div></header><section class="panel__content"><div class="container"><article class="article" data-shortcut-switcher="inactive"><h1 data-toc="cloneRole" id="cloneRole.topic">Clone role</h1><p id="-w5ri97_2">This endpoint documentation is generated directly from <code class="code" id="-w5ri97_8">openapi.yaml</code>.</p><section class="chapter"><h2 id="endpoint" data-toc="endpoint">Endpoint</h2><div class="code-block" data-lang="http">POST /roles/clone</div></section><section class="chapter"><h2 id="operation" data-toc="operation">Operation</h2><p id="-w5ri97_10">Operation ID: <code class="code" id="-w5ri97_12">cloneRole</code></p><p id="-w5ri97_11">Clone role</p></section><section class="chapter"><h2 id="authentication" data-toc="authentication">Authentication</h2><p id="-w5ri97_13">Security requirements:</p><div class="table-wrapper"><table class="wide" id="-w5ri97_14"><thead><tr class="ijRowHead" id="-w5ri97_15"><th id="-w5ri97_17"><p>Scheme</p></th><th id="-w5ri97_18"><p>Scopes</p></th></tr></thead><tbody><tr id="-w5ri97_16"><td id="-w5ri97_19"><p>BearerAuth</p></td><td id="-w5ri97_20"><p>-</p></td></tr></tbody></table></div></section><section class="chapter"><h2 id="request-body" data-toc="request-body">Request Body</h2><p id="-w5ri97_21">Required: yes.</p><p id="-w5ri97_22">Content type: <code class="code" id="-w5ri97_24">application/json</code></p><div class="code-block" data-lang="json">
{
&quot;properties&quot;: {
&quot;name&quot;: {
&quot;type&quot;: &quot;string&quot;
},
&quot;role_id&quot;: {
&quot;type&quot;: &quot;integer&quot;
}
},
&quot;required&quot;: [
&quot;role_id&quot;,
&quot;name&quot;
],
&quot;type&quot;: &quot;object&quot;
}
</div></section><section class="chapter"><h2 id="responses" data-toc="responses">Responses</h2><div class="table-wrapper"><table class="wide" id="-w5ri97_25"><thead><tr class="ijRowHead" id="-w5ri97_28"><th id="-w5ri97_30"><p>Status</p></th><th id="-w5ri97_31"><p>Description</p></th><th id="-w5ri97_32"><p>Content Types</p></th></tr></thead><tbody><tr id="-w5ri97_29"><td id="-w5ri97_33"><p>200</p></td><td id="-w5ri97_34"><p>Success</p></td><td id="-w5ri97_35"><p>application/json</p></td></tr></tbody></table></div><p id="-w5ri97_26">Schema for response <code class="code" id="-w5ri97_36">200</code> (<code class="code" id="-w5ri97_37">application/json</code>):</p><div class="code-block" data-lang="json">
{}
</div></section><div class="last-modified">17 March 2026</div><div data-feedback-placeholder="true"></div><div class="navigation-links _bottom"><a href="editrole.html" class="navigation-links__prev">Edit role</a><a href="removerolepermission.html" class="navigation-links__next">Remove permission from role</a></div></article><div id="disqus_thread"></div></div></section></main></div><script src="https://resources.jetbrains.com/writerside/apidoc/6.26.0-b913/app.js"></script></body></html>
@@ -0,0 +1,27 @@
<!DOCTYPE html SYSTEM "about:legacy-compat">
<html lang="en-US" data-preset="contrast" data-primary-color="#307FFF"><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8"><meta charset="UTF-8"><meta name="robots" content="noindex"><meta name="built-on" content="2026-03-17T15:43:46.657738149"><title>Close draft invoice | Copenhagen Truck Wash API</title><script type="application/json" id="virtual-toc-data">[{"id":"endpoint","level":0,"title":"Endpoint","anchor":"#endpoint"},{"id":"operation","level":0,"title":"Operation","anchor":"#operation"},{"id":"authentication","level":0,"title":"Authentication","anchor":"#authentication"},{"id":"request-body","level":0,"title":"Request Body","anchor":"#request-body"},{"id":"responses","level":0,"title":"Responses","anchor":"#responses"}]</script><script type="application/json" id="topic-shortcuts"></script><link href="https://resources.jetbrains.com/writerside/apidoc/6.26.0-b913/app.css" rel="stylesheet"><meta name="msapplication-TileColor" content="#000000"><link rel="apple-touch-icon" sizes="180x180" href="https://jetbrains.com/apple-touch-icon.png"><link rel="icon" type="image/png" sizes="32x32" href="https://jetbrains.com/favicon-32x32.png"><link rel="icon" type="image/png" sizes="16x16" href="https://jetbrains.com/favicon-16x16.png"><meta name="msapplication-TileImage" content="https://resources.jetbrains.com/storage/ui/favicons/mstile-144x144.png"><meta name="msapplication-square70x70logo" content="https://resources.jetbrains.com/storage/ui/favicons/mstile-70x70.png"><meta name="msapplication-square150x150logo" content="https://resources.jetbrains.com/storage/ui/favicons/mstile-150x150.png"><meta name="msapplication-wide310x150logo" content="https://resources.jetbrains.com/storage/ui/favicons/mstile-310x150.png"><meta name="msapplication-square310x310logo" content="https://resources.jetbrains.com/storage/ui/favicons/mstile-310x310.png"><meta name="image" content=""><!-- Open Graph --><meta property="og:title" content="Close draft invoice | Copenhagen Truck Wash API"><meta property="og:description" content=""><meta property="og:image" content=""><meta property="og:site_name" content="Copenhagen Truck Wash API Help"><meta property="og:type" content="website"><meta property="og:locale" content="en_US"><meta property="og:url" content="writerside-documentation/closedraftinvoice.html"><!-- End Open Graph --><!-- Twitter Card --><meta name="twitter:card" content="summary_large_image"><meta name="twitter:site" content=""><meta name="twitter:title" content="Close draft invoice | Copenhagen Truck Wash API"><meta name="twitter:description" content=""><meta name="twitter:creator" content=""><meta name="twitter:image:src" content=""><!-- End Twitter Card --><!-- Schema.org WebPage --><script type="application/ld+json">{
"@context": "http://schema.org",
"@type": "WebPage",
"@id": "writerside-documentation/closedraftinvoice.html#webpage",
"url": "writerside-documentation/closedraftinvoice.html",
"name": "Close draft invoice | Copenhagen Truck Wash API",
"description": "",
"image": "",
"inLanguage":"en-US"
}</script><!-- End Schema.org --><!-- Schema.org WebSite --><script type="application/ld+json">{
"@type": "WebSite",
"@id": "writerside-documentation/#website",
"url": "writerside-documentation/",
"name": "Copenhagen Truck Wash API Help"
}</script><!-- End Schema.org --></head><body data-id="closeDraftInvoice" data-main-title="Close draft invoice" data-article-props="{&quot;seeAlsoStyle&quot;:&quot;links&quot;}" data-template="article" data-breadcrumbs="API-Reference.topic|API Reference///Tag_Invoices.topic|Invoices///Tag_Invoices_Page_1.topic|Invoices - Page 1 of 2"><div class="wrapper"><main class="panel _main"><header class="panel__header"><div class="container"><h3>Copenhagen Truck Wash API Help</h3><div class="panel-trigger"></div></div></header><section class="panel__content"><div class="container"><article class="article" data-shortcut-switcher="inactive"><h1 data-toc="closeDraftInvoice" id="closeDraftInvoice.topic">Close draft invoice</h1><p id="-ogm8ze_2">This endpoint documentation is generated directly from <code class="code" id="-ogm8ze_8">openapi.yaml</code>.</p><section class="chapter"><h2 id="endpoint" data-toc="endpoint">Endpoint</h2><div class="code-block" data-lang="http">POST /invoices/draft/close</div></section><section class="chapter"><h2 id="operation" data-toc="operation">Operation</h2><p id="-ogm8ze_10">Operation ID: <code class="code" id="-ogm8ze_12">closeDraftInvoice</code></p><p id="-ogm8ze_11">Close a draft invoice</p></section><section class="chapter"><h2 id="authentication" data-toc="authentication">Authentication</h2><p id="-ogm8ze_13">Security requirements:</p><div class="table-wrapper"><table class="wide" id="-ogm8ze_14"><thead><tr class="ijRowHead" id="-ogm8ze_15"><th id="-ogm8ze_17"><p>Scheme</p></th><th id="-ogm8ze_18"><p>Scopes</p></th></tr></thead><tbody><tr id="-ogm8ze_16"><td id="-ogm8ze_19"><p>BearerAuth</p></td><td id="-ogm8ze_20"><p>-</p></td></tr></tbody></table></div></section><section class="chapter"><h2 id="request-body" data-toc="request-body">Request Body</h2><p id="-ogm8ze_21">Required: yes.</p><p id="-ogm8ze_22">Content type: <code class="code" id="-ogm8ze_24">application/json</code></p><div class="code-block" data-lang="json">
{
&quot;properties&quot;: {
&quot;id&quot;: {
&quot;type&quot;: &quot;integer&quot;
}
},
&quot;type&quot;: &quot;object&quot;
}
</div></section><section class="chapter"><h2 id="responses" data-toc="responses">Responses</h2><div class="table-wrapper"><table class="wide" id="-ogm8ze_25"><thead><tr class="ijRowHead" id="-ogm8ze_28"><th id="-ogm8ze_30"><p>Status</p></th><th id="-ogm8ze_31"><p>Description</p></th><th id="-ogm8ze_32"><p>Content Types</p></th></tr></thead><tbody><tr id="-ogm8ze_29"><td id="-ogm8ze_33"><p>200</p></td><td id="-ogm8ze_34"><p>Draft invoice closed successfully</p></td><td id="-ogm8ze_35"><p>application/json</p></td></tr></tbody></table></div><p id="-ogm8ze_26">Schema for response <code class="code" id="-ogm8ze_36">200</code> (<code class="code" id="-ogm8ze_37">application/json</code>):</p><div class="code-block" data-lang="json">
{}
</div></section><div class="last-modified">17 March 2026</div><div data-feedback-placeholder="true"></div><div class="navigation-links _bottom"><a href="listdraftinvoices.html" class="navigation-links__prev">List draft invoices</a><a href="getinvoicepdf.html" class="navigation-links__next">Get invoice PDF</a></div></article><div id="disqus_thread"></div></div></section></main></div><script src="https://resources.jetbrains.com/writerside/apidoc/6.26.0-b913/app.js"></script></body></html>
@@ -0,0 +1,20 @@
<!DOCTYPE html SYSTEM "about:legacy-compat">
<html lang="en-US" data-preset="contrast" data-primary-color="#307FFF"><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8"><meta charset="UTF-8"><meta name="robots" content="noindex"><meta name="built-on" content="2026-03-17T15:41:35.418037854"><title>Compare collected invoice totals with E-conomic | Copenhagen Truck Wash API</title><script type="application/json" id="virtual-toc-data">[{"id":"endpoint","level":0,"title":"Endpoint","anchor":"#endpoint"},{"id":"operation","level":0,"title":"Operation","anchor":"#operation"},{"id":"authentication","level":0,"title":"Authentication","anchor":"#authentication"},{"id":"parameters","level":0,"title":"Parameters","anchor":"#parameters"},{"id":"responses","level":0,"title":"Responses","anchor":"#responses"}]</script><script type="application/json" id="topic-shortcuts"></script><link href="https://resources.jetbrains.com/writerside/apidoc/6.26.0-b913/app.css" rel="stylesheet"><meta name="msapplication-TileColor" content="#000000"><link rel="apple-touch-icon" sizes="180x180" href="https://jetbrains.com/apple-touch-icon.png"><link rel="icon" type="image/png" sizes="32x32" href="https://jetbrains.com/favicon-32x32.png"><link rel="icon" type="image/png" sizes="16x16" href="https://jetbrains.com/favicon-16x16.png"><meta name="msapplication-TileImage" content="https://resources.jetbrains.com/storage/ui/favicons/mstile-144x144.png"><meta name="msapplication-square70x70logo" content="https://resources.jetbrains.com/storage/ui/favicons/mstile-70x70.png"><meta name="msapplication-square150x150logo" content="https://resources.jetbrains.com/storage/ui/favicons/mstile-150x150.png"><meta name="msapplication-wide310x150logo" content="https://resources.jetbrains.com/storage/ui/favicons/mstile-310x150.png"><meta name="msapplication-square310x310logo" content="https://resources.jetbrains.com/storage/ui/favicons/mstile-310x310.png"><meta name="image" content=""><!-- Open Graph --><meta property="og:title" content="Compare collected invoice totals with E-conomic | Copenhagen Truck Wash API"><meta property="og:description" content=""><meta property="og:image" content=""><meta property="og:site_name" content="Copenhagen Truck Wash API Help"><meta property="og:type" content="website"><meta property="og:locale" content="en_US"><meta property="og:url" content="writerside-documentation/comparecollectedinvoiceeconomic.html"><!-- End Open Graph --><!-- Twitter Card --><meta name="twitter:card" content="summary_large_image"><meta name="twitter:site" content=""><meta name="twitter:title" content="Compare collected invoice totals with E-conomic | Copenhagen Truck Wash API"><meta name="twitter:description" content=""><meta name="twitter:creator" content=""><meta name="twitter:image:src" content=""><!-- End Twitter Card --><!-- Schema.org WebPage --><script type="application/ld+json">{
"@context": "http://schema.org",
"@type": "WebPage",
"@id": "writerside-documentation/comparecollectedinvoiceeconomic.html#webpage",
"url": "writerside-documentation/comparecollectedinvoiceeconomic.html",
"name": "Compare collected invoice totals with E-conomic | Copenhagen Truck Wash API",
"description": "",
"image": "",
"inLanguage":"en-US"
}</script><!-- End Schema.org --><!-- Schema.org WebSite --><script type="application/ld+json">{
"@type": "WebSite",
"@id": "writerside-documentation/#website",
"url": "writerside-documentation/",
"name": "Copenhagen Truck Wash API Help"
}</script><!-- End Schema.org --></head><body data-id="compareCollectedInvoiceEconomic" data-main-title="Compare collected invoice totals with E-conomic" data-article-props="{&quot;seeAlsoStyle&quot;:&quot;links&quot;}" data-template="article" data-breadcrumbs="API-Reference.topic|API Reference///Tag_Invoices.topic|Invoices///Tag_Invoices_Page_1.topic|Invoices - Page 1 of 2"><div class="wrapper"><main class="panel _main"><header class="panel__header"><div class="container"><h3>Copenhagen Truck Wash API Help</h3><div class="panel-trigger"></div></div></header><section class="panel__content"><div class="container"><article class="article" data-shortcut-switcher="inactive"><h1 data-toc="compareCollectedInvoiceEconomic" id="compareCollectedInvoiceEconomic.topic">Compare collected invoice totals with E-conomic</h1><p id="z8n6lc_2">This endpoint documentation is generated directly from <code class="code" id="z8n6lc_8">openapi.yaml</code>.</p><section class="chapter"><h2 id="endpoint" data-toc="endpoint">Endpoint</h2><div class="code-block" data-lang="http">GET /collected-invoices/economic/compare</div></section><section class="chapter"><h2 id="operation" data-toc="operation">Operation</h2><p id="z8n6lc_10">Operation ID: <code class="code" id="z8n6lc_12">compareCollectedInvoiceEconomic</code></p><p id="z8n6lc_11">Compares a collected invoice in the system with its corresponding invoice in E-conomic. Returns totals from both sources, their difference, and any warnings detected during comparison.</p></section><section class="chapter"><h2 id="authentication" data-toc="authentication">Authentication</h2><p id="z8n6lc_13">Security requirements:</p><div class="table-wrapper"><table class="wide" id="z8n6lc_14"><thead><tr class="ijRowHead" id="z8n6lc_15"><th id="z8n6lc_17"><p>Scheme</p></th><th id="z8n6lc_18"><p>Scopes</p></th></tr></thead><tbody><tr id="z8n6lc_16"><td id="z8n6lc_19"><p>BearerAuth</p></td><td id="z8n6lc_20"><p>-</p></td></tr></tbody></table></div></section><section class="chapter"><h2 id="parameters" data-toc="parameters">Parameters</h2><div class="table-wrapper"><table class="wide" id="z8n6lc_21"><thead><tr class="ijRowHead" id="z8n6lc_22"><th id="z8n6lc_24"><p>Name</p></th><th id="z8n6lc_25"><p>In</p></th><th id="z8n6lc_26"><p>Required</p></th><th id="z8n6lc_27"><p>Type</p></th><th id="z8n6lc_28"><p>Description</p></th></tr></thead><tbody><tr id="z8n6lc_23"><td id="z8n6lc_29"><p>collected_invoice_id</p></td><td id="z8n6lc_30"><p>query</p></td><td id="z8n6lc_31"><p>yes</p></td><td id="z8n6lc_32"><p>integer</p></td><td id="z8n6lc_33"><p>The internal collected invoice ID to compare</p></td></tr></tbody></table></div></section><section class="chapter"><h2 id="responses" data-toc="responses">Responses</h2><div class="table-wrapper"><table class="wide" id="z8n6lc_34"><thead><tr class="ijRowHead" id="z8n6lc_37"><th id="z8n6lc_44"><p>Status</p></th><th id="z8n6lc_45"><p>Description</p></th><th id="z8n6lc_46"><p>Content Types</p></th></tr></thead><tbody><tr id="z8n6lc_38"><td id="z8n6lc_47"><p>200</p></td><td id="z8n6lc_48"><p>Comparison completed successfully</p></td><td id="z8n6lc_49"><p>application/json</p></td></tr><tr id="z8n6lc_39"><td id="z8n6lc_50"><p>400</p></td><td id="z8n6lc_51"></td><td id="z8n6lc_52"></td></tr><tr id="z8n6lc_40"><td id="z8n6lc_53"><p>401</p></td><td id="z8n6lc_54"></td><td id="z8n6lc_55"></td></tr><tr id="z8n6lc_41"><td id="z8n6lc_56"><p>403</p></td><td id="z8n6lc_57"></td><td id="z8n6lc_58"></td></tr><tr id="z8n6lc_42"><td id="z8n6lc_59"><p>404</p></td><td id="z8n6lc_60"></td><td id="z8n6lc_61"></td></tr><tr id="z8n6lc_43"><td id="z8n6lc_62"><p>500</p></td><td id="z8n6lc_63"></td><td id="z8n6lc_64"></td></tr></tbody></table></div><p id="z8n6lc_35">Schema for response <code class="code" id="z8n6lc_65">200</code> (<code class="code" id="z8n6lc_66">application/json</code>):</p><div class="code-block" data-lang="json">
{
&quot;$ref&quot;: &quot;#/components/schemas/CollectedInvoiceEconomicCompareResponse&quot;
}
</div></section><div class="last-modified">17 March 2026</div><div data-feedback-placeholder="true"></div><div class="navigation-links _bottom"><a href="updatecollectedinvoice.html" class="navigation-links__prev">Update collected invoice</a><a href="comparecollectedinvoiceeconomicv2.html" class="navigation-links__next">Compare internal invoice with draft/booked (V2)</a></div></article><div id="disqus_thread"></div></div></section></main></div><script src="https://resources.jetbrains.com/writerside/apidoc/6.26.0-b913/app.js"></script></body></html>
@@ -0,0 +1,20 @@
<!DOCTYPE html SYSTEM "about:legacy-compat">
<html lang="en-US" data-preset="contrast" data-primary-color="#307FFF"><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8"><meta charset="UTF-8"><meta name="robots" content="noindex"><meta name="built-on" content="2026-03-17T15:41:51.60352258"><title>Compare internal invoice with draft/booked (V2) | Copenhagen Truck Wash API</title><script type="application/json" id="virtual-toc-data">[{"id":"endpoint","level":0,"title":"Endpoint","anchor":"#endpoint"},{"id":"operation","level":0,"title":"Operation","anchor":"#operation"},{"id":"authentication","level":0,"title":"Authentication","anchor":"#authentication"},{"id":"parameters","level":0,"title":"Parameters","anchor":"#parameters"},{"id":"responses","level":0,"title":"Responses","anchor":"#responses"}]</script><script type="application/json" id="topic-shortcuts"></script><link href="https://resources.jetbrains.com/writerside/apidoc/6.26.0-b913/app.css" rel="stylesheet"><meta name="msapplication-TileColor" content="#000000"><link rel="apple-touch-icon" sizes="180x180" href="https://jetbrains.com/apple-touch-icon.png"><link rel="icon" type="image/png" sizes="32x32" href="https://jetbrains.com/favicon-32x32.png"><link rel="icon" type="image/png" sizes="16x16" href="https://jetbrains.com/favicon-16x16.png"><meta name="msapplication-TileImage" content="https://resources.jetbrains.com/storage/ui/favicons/mstile-144x144.png"><meta name="msapplication-square70x70logo" content="https://resources.jetbrains.com/storage/ui/favicons/mstile-70x70.png"><meta name="msapplication-square150x150logo" content="https://resources.jetbrains.com/storage/ui/favicons/mstile-150x150.png"><meta name="msapplication-wide310x150logo" content="https://resources.jetbrains.com/storage/ui/favicons/mstile-310x150.png"><meta name="msapplication-square310x310logo" content="https://resources.jetbrains.com/storage/ui/favicons/mstile-310x310.png"><meta name="image" content=""><!-- Open Graph --><meta property="og:title" content="Compare internal invoice with draft/booked (V2) | Copenhagen Truck Wash API"><meta property="og:description" content=""><meta property="og:image" content=""><meta property="og:site_name" content="Copenhagen Truck Wash API Help"><meta property="og:type" content="website"><meta property="og:locale" content="en_US"><meta property="og:url" content="writerside-documentation/comparecollectedinvoiceeconomicv2.html"><!-- End Open Graph --><!-- Twitter Card --><meta name="twitter:card" content="summary_large_image"><meta name="twitter:site" content=""><meta name="twitter:title" content="Compare internal invoice with draft/booked (V2) | Copenhagen Truck Wash API"><meta name="twitter:description" content=""><meta name="twitter:creator" content=""><meta name="twitter:image:src" content=""><!-- End Twitter Card --><!-- Schema.org WebPage --><script type="application/ld+json">{
"@context": "http://schema.org",
"@type": "WebPage",
"@id": "writerside-documentation/comparecollectedinvoiceeconomicv2.html#webpage",
"url": "writerside-documentation/comparecollectedinvoiceeconomicv2.html",
"name": "Compare internal invoice with draft/booked (V2) | Copenhagen Truck Wash API",
"description": "",
"image": "",
"inLanguage":"en-US"
}</script><!-- End Schema.org --><!-- Schema.org WebSite --><script type="application/ld+json">{
"@type": "WebSite",
"@id": "writerside-documentation/#website",
"url": "writerside-documentation/",
"name": "Copenhagen Truck Wash API Help"
}</script><!-- End Schema.org --></head><body data-id="compareCollectedInvoiceEconomicV2" data-main-title="Compare internal invoice with draft/booked (V2)" data-article-props="{&quot;seeAlsoStyle&quot;:&quot;links&quot;}" data-template="article" data-breadcrumbs="API-Reference.topic|API Reference///Tag_Invoices.topic|Invoices///Tag_Invoices_Page_1.topic|Invoices - Page 1 of 2"><div class="wrapper"><main class="panel _main"><header class="panel__header"><div class="container"><h3>Copenhagen Truck Wash API Help</h3><div class="panel-trigger"></div></div></header><section class="panel__content"><div class="container"><article class="article" data-shortcut-switcher="inactive"><h1 data-toc="compareCollectedInvoiceEconomicV2" id="compareCollectedInvoiceEconomicV2.topic">Compare internal invoice with draft/booked (V2)</h1><p id="-4kx62s_2">This endpoint documentation is generated directly from <code class="code" id="-4kx62s_8">openapi.yaml</code>.</p><section class="chapter"><h2 id="endpoint" data-toc="endpoint">Endpoint</h2><div class="code-block" data-lang="http">GET /collected-invoices/economic/v2/compare</div></section><section class="chapter"><h2 id="operation" data-toc="operation">Operation</h2><p id="-4kx62s_10">Operation ID: <code class="code" id="-4kx62s_12">compareCollectedInvoiceEconomicV2</code></p><p id="-4kx62s_11">Compare internal invoice with draft/booked (V2)</p></section><section class="chapter"><h2 id="authentication" data-toc="authentication">Authentication</h2><p id="-4kx62s_13">Security requirements:</p><div class="table-wrapper"><table class="wide" id="-4kx62s_14"><thead><tr class="ijRowHead" id="-4kx62s_15"><th id="-4kx62s_17"><p>Scheme</p></th><th id="-4kx62s_18"><p>Scopes</p></th></tr></thead><tbody><tr id="-4kx62s_16"><td id="-4kx62s_19"><p>BearerAuth</p></td><td id="-4kx62s_20"><p>-</p></td></tr></tbody></table></div></section><section class="chapter"><h2 id="parameters" data-toc="parameters">Parameters</h2><div class="table-wrapper"><table class="wide" id="-4kx62s_21"><thead><tr class="ijRowHead" id="-4kx62s_22"><th id="-4kx62s_24"><p>Name</p></th><th id="-4kx62s_25"><p>In</p></th><th id="-4kx62s_26"><p>Required</p></th><th id="-4kx62s_27"><p>Type</p></th><th id="-4kx62s_28"><p>Description</p></th></tr></thead><tbody><tr id="-4kx62s_23"><td id="-4kx62s_29"><p>collected_invoice_id</p></td><td id="-4kx62s_30"><p>query</p></td><td id="-4kx62s_31"><p>yes</p></td><td id="-4kx62s_32"><p>integer</p></td><td id="-4kx62s_33"></td></tr></tbody></table></div></section><section class="chapter"><h2 id="responses" data-toc="responses">Responses</h2><div class="table-wrapper"><table class="wide" id="-4kx62s_34"><thead><tr class="ijRowHead" id="-4kx62s_37"><th id="-4kx62s_44"><p>Status</p></th><th id="-4kx62s_45"><p>Description</p></th><th id="-4kx62s_46"><p>Content Types</p></th></tr></thead><tbody><tr id="-4kx62s_38"><td id="-4kx62s_47"><p>200</p></td><td id="-4kx62s_48"><p>Comparison completed</p></td><td id="-4kx62s_49"><p>application/json</p></td></tr><tr id="-4kx62s_39"><td id="-4kx62s_50"><p>400</p></td><td id="-4kx62s_51"></td><td id="-4kx62s_52"></td></tr><tr id="-4kx62s_40"><td id="-4kx62s_53"><p>401</p></td><td id="-4kx62s_54"></td><td id="-4kx62s_55"></td></tr><tr id="-4kx62s_41"><td id="-4kx62s_56"><p>403</p></td><td id="-4kx62s_57"></td><td id="-4kx62s_58"></td></tr><tr id="-4kx62s_42"><td id="-4kx62s_59"><p>404</p></td><td id="-4kx62s_60"></td><td id="-4kx62s_61"></td></tr><tr id="-4kx62s_43"><td id="-4kx62s_62"><p>500</p></td><td id="-4kx62s_63"></td><td id="-4kx62s_64"></td></tr></tbody></table></div><p id="-4kx62s_35">Schema for response <code class="code" id="-4kx62s_65">200</code> (<code class="code" id="-4kx62s_66">application/json</code>):</p><div class="code-block" data-lang="json">
{
&quot;$ref&quot;: &quot;#/components/schemas/CollectedInvoiceEconomicV2CompareResponse&quot;
}
</div></section><div class="last-modified">17 March 2026</div><div data-feedback-placeholder="true"></div><div class="navigation-links _bottom"><a href="comparecollectedinvoiceeconomic.html" class="navigation-links__prev">Compare collected invoice totals with E-conomic</a><a href="comparecollectedinvoiceeconomicv2bulk.html" class="navigation-links__next">Bulk compare collected invoices against draft/booked (V2)</a></div></article><div id="disqus_thread"></div></div></section></main></div><script src="https://resources.jetbrains.com/writerside/apidoc/6.26.0-b913/app.js"></script></body></html>
@@ -0,0 +1,38 @@
<!DOCTYPE html SYSTEM "about:legacy-compat">
<html lang="en-US" data-preset="contrast" data-primary-color="#307FFF"><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8"><meta charset="UTF-8"><meta name="robots" content="noindex"><meta name="built-on" content="2026-03-17T15:42:03.234108135"><title>Bulk compare collected invoices against draft/booked (V2) | Copenhagen Truck Wash API</title><script type="application/json" id="virtual-toc-data">[{"id":"endpoint","level":0,"title":"Endpoint","anchor":"#endpoint"},{"id":"operation","level":0,"title":"Operation","anchor":"#operation"},{"id":"authentication","level":0,"title":"Authentication","anchor":"#authentication"},{"id":"request-body","level":0,"title":"Request Body","anchor":"#request-body"},{"id":"responses","level":0,"title":"Responses","anchor":"#responses"}]</script><script type="application/json" id="topic-shortcuts"></script><link href="https://resources.jetbrains.com/writerside/apidoc/6.26.0-b913/app.css" rel="stylesheet"><meta name="msapplication-TileColor" content="#000000"><link rel="apple-touch-icon" sizes="180x180" href="https://jetbrains.com/apple-touch-icon.png"><link rel="icon" type="image/png" sizes="32x32" href="https://jetbrains.com/favicon-32x32.png"><link rel="icon" type="image/png" sizes="16x16" href="https://jetbrains.com/favicon-16x16.png"><meta name="msapplication-TileImage" content="https://resources.jetbrains.com/storage/ui/favicons/mstile-144x144.png"><meta name="msapplication-square70x70logo" content="https://resources.jetbrains.com/storage/ui/favicons/mstile-70x70.png"><meta name="msapplication-square150x150logo" content="https://resources.jetbrains.com/storage/ui/favicons/mstile-150x150.png"><meta name="msapplication-wide310x150logo" content="https://resources.jetbrains.com/storage/ui/favicons/mstile-310x150.png"><meta name="msapplication-square310x310logo" content="https://resources.jetbrains.com/storage/ui/favicons/mstile-310x310.png"><meta name="image" content=""><!-- Open Graph --><meta property="og:title" content="Bulk compare collected invoices against draft/booked (V2) | Copenhagen Truck Wash API"><meta property="og:description" content=""><meta property="og:image" content=""><meta property="og:site_name" content="Copenhagen Truck Wash API Help"><meta property="og:type" content="website"><meta property="og:locale" content="en_US"><meta property="og:url" content="writerside-documentation/comparecollectedinvoiceeconomicv2bulk.html"><!-- End Open Graph --><!-- Twitter Card --><meta name="twitter:card" content="summary_large_image"><meta name="twitter:site" content=""><meta name="twitter:title" content="Bulk compare collected invoices against draft/booked (V2) | Copenhagen Truck Wash API"><meta name="twitter:description" content=""><meta name="twitter:creator" content=""><meta name="twitter:image:src" content=""><!-- End Twitter Card --><!-- Schema.org WebPage --><script type="application/ld+json">{
"@context": "http://schema.org",
"@type": "WebPage",
"@id": "writerside-documentation/comparecollectedinvoiceeconomicv2bulk.html#webpage",
"url": "writerside-documentation/comparecollectedinvoiceeconomicv2bulk.html",
"name": "Bulk compare collected invoices against draft/booked (V2) | Copenhagen Truck Wash API",
"description": "",
"image": "",
"inLanguage":"en-US"
}</script><!-- End Schema.org --><!-- Schema.org WebSite --><script type="application/ld+json">{
"@type": "WebSite",
"@id": "writerside-documentation/#website",
"url": "writerside-documentation/",
"name": "Copenhagen Truck Wash API Help"
}</script><!-- End Schema.org --></head><body data-id="compareCollectedInvoiceEconomicV2Bulk" data-main-title="Bulk compare collected invoices against draft/booked (V2)" data-article-props="{&quot;seeAlsoStyle&quot;:&quot;links&quot;}" data-template="article" data-breadcrumbs="API-Reference.topic|API Reference///Tag_Invoices.topic|Invoices///Tag_Invoices_Page_1.topic|Invoices - Page 1 of 2"><div class="wrapper"><main class="panel _main"><header class="panel__header"><div class="container"><h3>Copenhagen Truck Wash API Help</h3><div class="panel-trigger"></div></div></header><section class="panel__content"><div class="container"><article class="article" data-shortcut-switcher="inactive"><h1 data-toc="compareCollectedInvoiceEconomicV2Bulk" id="compareCollectedInvoiceEconomicV2Bulk.topic">Bulk compare collected invoices against draft/booked (V2)</h1><p id="sjnoz2_2">This endpoint documentation is generated directly from <code class="code" id="sjnoz2_8">openapi.yaml</code>.</p><section class="chapter"><h2 id="endpoint" data-toc="endpoint">Endpoint</h2><div class="code-block" data-lang="http">POST /collected-invoices/economic/v2/compare/bulk</div></section><section class="chapter"><h2 id="operation" data-toc="operation">Operation</h2><p id="sjnoz2_10">Operation ID: <code class="code" id="sjnoz2_12">compareCollectedInvoiceEconomicV2Bulk</code></p><p id="sjnoz2_11">Bulk compare collected invoices against draft/booked (V2)</p></section><section class="chapter"><h2 id="authentication" data-toc="authentication">Authentication</h2><p id="sjnoz2_13">Security requirements:</p><div class="table-wrapper"><table class="wide" id="sjnoz2_14"><thead><tr class="ijRowHead" id="sjnoz2_15"><th id="sjnoz2_17"><p>Scheme</p></th><th id="sjnoz2_18"><p>Scopes</p></th></tr></thead><tbody><tr id="sjnoz2_16"><td id="sjnoz2_19"><p>BearerAuth</p></td><td id="sjnoz2_20"><p>-</p></td></tr></tbody></table></div></section><section class="chapter"><h2 id="request-body" data-toc="request-body">Request Body</h2><p id="sjnoz2_21">Required: yes.</p><p id="sjnoz2_22">Content type: <code class="code" id="sjnoz2_24">application/json</code></p><div class="code-block" data-lang="json">
{
&quot;properties&quot;: {
&quot;collected_invoice_ids&quot;: {
&quot;items&quot;: {
&quot;minimum&quot;: 1,
&quot;type&quot;: &quot;integer&quot;
},
&quot;maxItems&quot;: 200,
&quot;minItems&quot;: 1,
&quot;type&quot;: &quot;array&quot;
}
},
&quot;required&quot;: [
&quot;collected_invoice_ids&quot;
],
&quot;type&quot;: &quot;object&quot;
}
</div></section><section class="chapter"><h2 id="responses" data-toc="responses">Responses</h2><div class="table-wrapper"><table class="wide" id="sjnoz2_25"><thead><tr class="ijRowHead" id="sjnoz2_28"><th id="sjnoz2_34"><p>Status</p></th><th id="sjnoz2_35"><p>Description</p></th><th id="sjnoz2_36"><p>Content Types</p></th></tr></thead><tbody><tr id="sjnoz2_29"><td id="sjnoz2_37"><p>200</p></td><td id="sjnoz2_38"><p>Bulk comparison completed</p></td><td id="sjnoz2_39"><p>application/json</p></td></tr><tr id="sjnoz2_30"><td id="sjnoz2_40"><p>400</p></td><td id="sjnoz2_41"></td><td id="sjnoz2_42"></td></tr><tr id="sjnoz2_31"><td id="sjnoz2_43"><p>401</p></td><td id="sjnoz2_44"></td><td id="sjnoz2_45"></td></tr><tr id="sjnoz2_32"><td id="sjnoz2_46"><p>403</p></td><td id="sjnoz2_47"></td><td id="sjnoz2_48"></td></tr><tr id="sjnoz2_33"><td id="sjnoz2_49"><p>500</p></td><td id="sjnoz2_50"></td><td id="sjnoz2_51"></td></tr></tbody></table></div><p id="sjnoz2_26">Schema for response <code class="code" id="sjnoz2_52">200</code> (<code class="code" id="sjnoz2_53">application/json</code>):</p><div class="code-block" data-lang="json">
{
&quot;$ref&quot;: &quot;#/components/schemas/CollectedInvoiceEconomicV2CompareBulkResponse&quot;
}
</div></section><div class="last-modified">17 March 2026</div><div data-feedback-placeholder="true"></div><div class="navigation-links _bottom"><a href="comparecollectedinvoiceeconomicv2.html" class="navigation-links__prev">Compare internal invoice with draft/booked (V2)</a><a href="getcollectedinvoiceeconomicv2details.html" class="navigation-links__next">Get deep V2 e-conomic invoice details</a></div></article><div id="disqus_thread"></div></div></section></main></div><script src="https://resources.jetbrains.com/writerside/apidoc/6.26.0-b913/app.js"></script></body></html>
@@ -0,0 +1,33 @@
<!DOCTYPE html SYSTEM "about:legacy-compat">
<html lang="en-US" data-preset="contrast" data-primary-color="#307FFF"><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8"><meta charset="UTF-8"><meta name="robots" content="noindex"><meta name="built-on" content="2026-03-17T15:42:05.74031022"><title>Complete order booking | Copenhagen Truck Wash API</title><script type="application/json" id="virtual-toc-data">[{"id":"endpoint","level":0,"title":"Endpoint","anchor":"#endpoint"},{"id":"operation","level":0,"title":"Operation","anchor":"#operation"},{"id":"authentication","level":0,"title":"Authentication","anchor":"#authentication"},{"id":"request-body","level":0,"title":"Request Body","anchor":"#request-body"},{"id":"responses","level":0,"title":"Responses","anchor":"#responses"}]</script><script type="application/json" id="topic-shortcuts"></script><link href="https://resources.jetbrains.com/writerside/apidoc/6.26.0-b913/app.css" rel="stylesheet"><meta name="msapplication-TileColor" content="#000000"><link rel="apple-touch-icon" sizes="180x180" href="https://jetbrains.com/apple-touch-icon.png"><link rel="icon" type="image/png" sizes="32x32" href="https://jetbrains.com/favicon-32x32.png"><link rel="icon" type="image/png" sizes="16x16" href="https://jetbrains.com/favicon-16x16.png"><meta name="msapplication-TileImage" content="https://resources.jetbrains.com/storage/ui/favicons/mstile-144x144.png"><meta name="msapplication-square70x70logo" content="https://resources.jetbrains.com/storage/ui/favicons/mstile-70x70.png"><meta name="msapplication-square150x150logo" content="https://resources.jetbrains.com/storage/ui/favicons/mstile-150x150.png"><meta name="msapplication-wide310x150logo" content="https://resources.jetbrains.com/storage/ui/favicons/mstile-310x150.png"><meta name="msapplication-square310x310logo" content="https://resources.jetbrains.com/storage/ui/favicons/mstile-310x310.png"><meta name="image" content=""><!-- Open Graph --><meta property="og:title" content="Complete order booking | Copenhagen Truck Wash API"><meta property="og:description" content=""><meta property="og:image" content=""><meta property="og:site_name" content="Copenhagen Truck Wash API Help"><meta property="og:type" content="website"><meta property="og:locale" content="en_US"><meta property="og:url" content="writerside-documentation/completeorderbooking.html"><!-- End Open Graph --><!-- Twitter Card --><meta name="twitter:card" content="summary_large_image"><meta name="twitter:site" content=""><meta name="twitter:title" content="Complete order booking | Copenhagen Truck Wash API"><meta name="twitter:description" content=""><meta name="twitter:creator" content=""><meta name="twitter:image:src" content=""><!-- End Twitter Card --><!-- Schema.org WebPage --><script type="application/ld+json">{
"@context": "http://schema.org",
"@type": "WebPage",
"@id": "writerside-documentation/completeorderbooking.html#webpage",
"url": "writerside-documentation/completeorderbooking.html",
"name": "Complete order booking | Copenhagen Truck Wash API",
"description": "",
"image": "",
"inLanguage":"en-US"
}</script><!-- End Schema.org --><!-- Schema.org WebSite --><script type="application/ld+json">{
"@type": "WebSite",
"@id": "writerside-documentation/#website",
"url": "writerside-documentation/",
"name": "Copenhagen Truck Wash API Help"
}</script><!-- End Schema.org --></head><body data-id="completeOrderBooking" data-main-title="Complete order booking" data-article-props="{&quot;seeAlsoStyle&quot;:&quot;links&quot;}" data-template="article" data-breadcrumbs="API-Reference.topic|API Reference///Tag_Bookings.topic|Bookings///Tag_Bookings_Page_1.topic|Bookings - Page 1 of 1"><div class="wrapper"><main class="panel _main"><header class="panel__header"><div class="container"><h3>Copenhagen Truck Wash API Help</h3><div class="panel-trigger"></div></div></header><section class="panel__content"><div class="container"><article class="article" data-shortcut-switcher="inactive"><h1 data-toc="completeOrderBooking" id="completeOrderBooking.topic">Complete order booking</h1><p id="z2cdn44_2">This endpoint documentation is generated directly from <code class="code" id="z2cdn44_8">openapi.yaml</code>.</p><section class="chapter"><h2 id="endpoint" data-toc="endpoint">Endpoint</h2><div class="code-block" data-lang="http">POST /order-bookings/complete</div></section><section class="chapter"><h2 id="operation" data-toc="operation">Operation</h2><p id="z2cdn44_10">Operation ID: <code class="code" id="z2cdn44_12">completeOrderBooking</code></p><p id="z2cdn44_11">Complete order booking</p></section><section class="chapter"><h2 id="authentication" data-toc="authentication">Authentication</h2><p id="z2cdn44_13">Security requirements:</p><div class="table-wrapper"><table class="wide" id="z2cdn44_14"><thead><tr class="ijRowHead" id="z2cdn44_15"><th id="z2cdn44_17"><p>Scheme</p></th><th id="z2cdn44_18"><p>Scopes</p></th></tr></thead><tbody><tr id="z2cdn44_16"><td id="z2cdn44_19"><p>BearerAuth</p></td><td id="z2cdn44_20"><p>-</p></td></tr></tbody></table></div></section><section class="chapter"><h2 id="request-body" data-toc="request-body">Request Body</h2><p id="z2cdn44_21">Required: yes.</p><p id="z2cdn44_22">Content type: <code class="code" id="z2cdn44_24">application/json</code></p><div class="code-block" data-lang="json">
{
&quot;properties&quot;: {
&quot;id&quot;: {
&quot;type&quot;: &quot;integer&quot;
},
&quot;safety_seal&quot;: {
&quot;type&quot;: &quot;integer&quot;
}
},
&quot;required&quot;: [
&quot;id&quot;
],
&quot;type&quot;: &quot;object&quot;
}
</div></section><section class="chapter"><h2 id="responses" data-toc="responses">Responses</h2><div class="table-wrapper"><table class="wide" id="z2cdn44_25"><thead><tr class="ijRowHead" id="z2cdn44_28"><th id="z2cdn44_30"><p>Status</p></th><th id="z2cdn44_31"><p>Description</p></th><th id="z2cdn44_32"><p>Content Types</p></th></tr></thead><tbody><tr id="z2cdn44_29"><td id="z2cdn44_33"><p>200</p></td><td id="z2cdn44_34"><p>Success</p></td><td id="z2cdn44_35"><p>application/json</p></td></tr></tbody></table></div><p id="z2cdn44_26">Schema for response <code class="code" id="z2cdn44_36">200</code> (<code class="code" id="z2cdn44_37">application/json</code>):</p><div class="code-block" data-lang="json">
{}
</div></section><div class="last-modified">17 March 2026</div><div data-feedback-placeholder="true"></div><div class="navigation-links _bottom"><a href="listorderbookings.html" class="navigation-links__prev">List order bookings</a><a href="syncallbookings.html" class="navigation-links__next">Sync all bookings from external system</a></div></article><div id="disqus_thread"></div></div></section></main></div><script src="https://resources.jetbrains.com/writerside/apidoc/6.26.0-b913/app.js"></script></body></html>
@@ -0,0 +1,64 @@
<!DOCTYPE html SYSTEM "about:legacy-compat">
<html lang="en-US" data-preset="contrast" data-primary-color="#307FFF"><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8"><meta charset="UTF-8"><meta name="robots" content="noindex"><meta name="built-on" content="2026-03-17T15:39:30.840083392"><title>Complete subuser setup | Copenhagen Truck Wash API</title><script type="application/json" id="virtual-toc-data">[{"id":"endpoint","level":0,"title":"Endpoint","anchor":"#endpoint"},{"id":"operation","level":0,"title":"Operation","anchor":"#operation"},{"id":"authentication","level":0,"title":"Authentication","anchor":"#authentication"},{"id":"request-body","level":0,"title":"Request Body","anchor":"#request-body"},{"id":"responses","level":0,"title":"Responses","anchor":"#responses"}]</script><script type="application/json" id="topic-shortcuts"></script><link href="https://resources.jetbrains.com/writerside/apidoc/6.26.0-b913/app.css" rel="stylesheet"><meta name="msapplication-TileColor" content="#000000"><link rel="apple-touch-icon" sizes="180x180" href="https://jetbrains.com/apple-touch-icon.png"><link rel="icon" type="image/png" sizes="32x32" href="https://jetbrains.com/favicon-32x32.png"><link rel="icon" type="image/png" sizes="16x16" href="https://jetbrains.com/favicon-16x16.png"><meta name="msapplication-TileImage" content="https://resources.jetbrains.com/storage/ui/favicons/mstile-144x144.png"><meta name="msapplication-square70x70logo" content="https://resources.jetbrains.com/storage/ui/favicons/mstile-70x70.png"><meta name="msapplication-square150x150logo" content="https://resources.jetbrains.com/storage/ui/favicons/mstile-150x150.png"><meta name="msapplication-wide310x150logo" content="https://resources.jetbrains.com/storage/ui/favicons/mstile-310x150.png"><meta name="msapplication-square310x310logo" content="https://resources.jetbrains.com/storage/ui/favicons/mstile-310x310.png"><meta name="image" content=""><!-- Open Graph --><meta property="og:title" content="Complete subuser setup | Copenhagen Truck Wash API"><meta property="og:description" content=""><meta property="og:image" content=""><meta property="og:site_name" content="Copenhagen Truck Wash API Help"><meta property="og:type" content="website"><meta property="og:locale" content="en_US"><meta property="og:url" content="writerside-documentation/completesubusersetup.html"><!-- End Open Graph --><!-- Twitter Card --><meta name="twitter:card" content="summary_large_image"><meta name="twitter:site" content=""><meta name="twitter:title" content="Complete subuser setup | Copenhagen Truck Wash API"><meta name="twitter:description" content=""><meta name="twitter:creator" content=""><meta name="twitter:image:src" content=""><!-- End Twitter Card --><!-- Schema.org WebPage --><script type="application/ld+json">{
"@context": "http://schema.org",
"@type": "WebPage",
"@id": "writerside-documentation/completesubusersetup.html#webpage",
"url": "writerside-documentation/completesubusersetup.html",
"name": "Complete subuser setup | Copenhagen Truck Wash API",
"description": "",
"image": "",
"inLanguage":"en-US"
}</script><!-- End Schema.org --><!-- Schema.org WebSite --><script type="application/ld+json">{
"@type": "WebSite",
"@id": "writerside-documentation/#website",
"url": "writerside-documentation/",
"name": "Copenhagen Truck Wash API Help"
}</script><!-- End Schema.org --></head><body data-id="completeSubuserSetup" data-main-title="Complete subuser setup" data-article-props="{&quot;seeAlsoStyle&quot;:&quot;links&quot;}" data-template="article" data-breadcrumbs="API-Reference.topic|API Reference///Tag_Subusers.topic|Subusers///Tag_Subusers_Page_1.topic|Subusers - Page 1 of 1"><div class="wrapper"><main class="panel _main"><header class="panel__header"><div class="container"><h3>Copenhagen Truck Wash API Help</h3><div class="panel-trigger"></div></div></header><section class="panel__content"><div class="container"><article class="article" data-shortcut-switcher="inactive"><h1 data-toc="completeSubuserSetup" id="completeSubuserSetup.topic">Complete subuser setup</h1><p id="-wal9f9_2">This endpoint documentation is generated directly from <code class="code" id="-wal9f9_8">openapi.yaml</code>.</p><section class="chapter"><h2 id="endpoint" data-toc="endpoint">Endpoint</h2><div class="code-block" data-lang="http">POST /subusers/setup</div></section><section class="chapter"><h2 id="operation" data-toc="operation">Operation</h2><p id="-wal9f9_10">Operation ID: <code class="code" id="-wal9f9_12">completeSubuserSetup</code></p><p id="-wal9f9_11">Completes subuser setup by setting a password and basic profile fields. Accepts optional `username` and `email`.</p></section><section class="chapter"><h2 id="authentication" data-toc="authentication">Authentication</h2><p id="-wal9f9_13">No authentication required.</p></section><section class="chapter"><h2 id="request-body" data-toc="request-body">Request Body</h2><p id="-wal9f9_14">Required: yes.</p><p id="-wal9f9_15">Content type: <code class="code" id="-wal9f9_17">application/json</code></p><div class="code-block" data-lang="json">
{
&quot;properties&quot;: {
&quot;email&quot;: {
&quot;format&quot;: &quot;email&quot;,
&quot;maxLength&quot;: 255,
&quot;minLength&quot;: 3,
&quot;type&quot;: &quot;string&quot;
},
&quot;name&quot;: {
&quot;maxLength&quot;: 255,
&quot;minLength&quot;: 3,
&quot;type&quot;: &quot;string&quot;
},
&quot;password&quot;: {
&quot;description&quot;: &quot;Must include at least one uppercase letter, one lowercase letter, and one number&quot;,
&quot;format&quot;: &quot;password&quot;,
&quot;minLength&quot;: 8,
&quot;pattern&quot;: &quot;^(?=.*[a-z])(?=.*[A-Z])(?=.*\\\\d).+$&quot;,
&quot;type&quot;: &quot;string&quot;
},
&quot;token&quot;: {
&quot;description&quot;: &quot;One-time setup token&quot;,
&quot;type&quot;: &quot;string&quot;
},
&quot;username&quot;: {
&quot;maxLength&quot;: 255,
&quot;minLength&quot;: 3,
&quot;type&quot;: &quot;string&quot;
}
},
&quot;required&quot;: [
&quot;token&quot;,
&quot;password&quot;,
&quot;name&quot;
],
&quot;type&quot;: &quot;object&quot;
}
</div></section><section class="chapter"><h2 id="responses" data-toc="responses">Responses</h2><div class="table-wrapper"><table class="wide" id="-wal9f9_18"><thead><tr class="ijRowHead" id="-wal9f9_21"><th id="-wal9f9_25"><p>Status</p></th><th id="-wal9f9_26"><p>Description</p></th><th id="-wal9f9_27"><p>Content Types</p></th></tr></thead><tbody><tr id="-wal9f9_22"><td id="-wal9f9_28"><p>200</p></td><td id="-wal9f9_29"><p>Setup completed</p></td><td id="-wal9f9_30"><p>application/json</p></td></tr><tr id="-wal9f9_23"><td id="-wal9f9_31"><p>400</p></td><td id="-wal9f9_32"></td><td id="-wal9f9_33"></td></tr><tr id="-wal9f9_24"><td id="-wal9f9_34"><p>500</p></td><td id="-wal9f9_35"></td><td id="-wal9f9_36"></td></tr></tbody></table></div><p id="-wal9f9_19">Schema for response <code class="code" id="-wal9f9_37">200</code> (<code class="code" id="-wal9f9_38">application/json</code>):</p><div class="code-block" data-lang="json">
{
&quot;properties&quot;: {
&quot;message&quot;: {
&quot;example&quot;: &quot;Password set successfully&quot;,
&quot;type&quot;: &quot;string&quot;
}
},
&quot;type&quot;: &quot;object&quot;
}
</div></section><div class="last-modified">17 March 2026</div><div data-feedback-placeholder="true"></div><div class="navigation-links _bottom"><a href="validatesubusersetuptoken.html" class="navigation-links__prev">Validate setup token</a><a href="getsubuser.html" class="navigation-links__next">Get a subuser by ID (visible by grant)</a></div></article><div id="disqus_thread"></div></div></section></main></div><script src="https://resources.jetbrains.com/writerside/apidoc/6.26.0-b913/app.js"></script></body></html>
@@ -0,0 +1,30 @@
<!DOCTYPE html SYSTEM "about:legacy-compat">
<html lang="en-US" data-preset="contrast" data-primary-color="#307FFF"><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8"><meta charset="UTF-8"><meta name="robots" content="noindex"><meta name="built-on" content="2026-03-17T15:39:29.8232138"><title>Complete wash without wash certificate | Copenhagen Truck Wash API</title><script type="application/json" id="virtual-toc-data">[{"id":"endpoint","level":0,"title":"Endpoint","anchor":"#endpoint"},{"id":"operation","level":0,"title":"Operation","anchor":"#operation"},{"id":"authentication","level":0,"title":"Authentication","anchor":"#authentication"},{"id":"request-body","level":0,"title":"Request Body","anchor":"#request-body"},{"id":"responses","level":0,"title":"Responses","anchor":"#responses"}]</script><script type="application/json" id="topic-shortcuts"></script><link href="https://resources.jetbrains.com/writerside/apidoc/6.26.0-b913/app.css" rel="stylesheet"><meta name="msapplication-TileColor" content="#000000"><link rel="apple-touch-icon" sizes="180x180" href="https://jetbrains.com/apple-touch-icon.png"><link rel="icon" type="image/png" sizes="32x32" href="https://jetbrains.com/favicon-32x32.png"><link rel="icon" type="image/png" sizes="16x16" href="https://jetbrains.com/favicon-16x16.png"><meta name="msapplication-TileImage" content="https://resources.jetbrains.com/storage/ui/favicons/mstile-144x144.png"><meta name="msapplication-square70x70logo" content="https://resources.jetbrains.com/storage/ui/favicons/mstile-70x70.png"><meta name="msapplication-square150x150logo" content="https://resources.jetbrains.com/storage/ui/favicons/mstile-150x150.png"><meta name="msapplication-wide310x150logo" content="https://resources.jetbrains.com/storage/ui/favicons/mstile-310x150.png"><meta name="msapplication-square310x310logo" content="https://resources.jetbrains.com/storage/ui/favicons/mstile-310x310.png"><meta name="image" content=""><!-- Open Graph --><meta property="og:title" content="Complete wash without wash certificate | Copenhagen Truck Wash API"><meta property="og:description" content=""><meta property="og:image" content=""><meta property="og:site_name" content="Copenhagen Truck Wash API Help"><meta property="og:type" content="website"><meta property="og:locale" content="en_US"><meta property="og:url" content="writerside-documentation/completewashwithoutwashcertificate.html"><!-- End Open Graph --><!-- Twitter Card --><meta name="twitter:card" content="summary_large_image"><meta name="twitter:site" content=""><meta name="twitter:title" content="Complete wash without wash certificate | Copenhagen Truck Wash API"><meta name="twitter:description" content=""><meta name="twitter:creator" content=""><meta name="twitter:image:src" content=""><!-- End Twitter Card --><!-- Schema.org WebPage --><script type="application/ld+json">{
"@context": "http://schema.org",
"@type": "WebPage",
"@id": "writerside-documentation/completewashwithoutwashcertificate.html#webpage",
"url": "writerside-documentation/completewashwithoutwashcertificate.html",
"name": "Complete wash without wash certificate | Copenhagen Truck Wash API",
"description": "",
"image": "",
"inLanguage":"en-US"
}</script><!-- End Schema.org --><!-- Schema.org WebSite --><script type="application/ld+json">{
"@type": "WebSite",
"@id": "writerside-documentation/#website",
"url": "writerside-documentation/",
"name": "Copenhagen Truck Wash API Help"
}</script><!-- End Schema.org --></head><body data-id="completeWashWithoutWashCertificate" data-main-title="Complete wash without wash certificate" data-article-props="{&quot;seeAlsoStyle&quot;:&quot;links&quot;}" data-template="article" data-breadcrumbs="API-Reference.topic|API Reference///Tag_Bookings.topic|Bookings///Tag_Bookings_Page_1.topic|Bookings - Page 1 of 1"><div class="wrapper"><main class="panel _main"><header class="panel__header"><div class="container"><h3>Copenhagen Truck Wash API Help</h3><div class="panel-trigger"></div></div></header><section class="panel__content"><div class="container"><article class="article" data-shortcut-switcher="inactive"><h1 data-toc="completeWashWithoutWashCertificate" id="completeWashWithoutWashCertificate.topic">Complete wash without wash certificate</h1><p id="ct5f6w_2">This endpoint documentation is generated directly from <code class="code" id="ct5f6w_8">openapi.yaml</code>.</p><section class="chapter"><h2 id="endpoint" data-toc="endpoint">Endpoint</h2><div class="code-block" data-lang="http">POST /admin/bookings/completeWashWithoutWashCertificate</div></section><section class="chapter"><h2 id="operation" data-toc="operation">Operation</h2><p id="ct5f6w_10">Operation ID: <code class="code" id="ct5f6w_12">completeWashWithoutWashCertificate</code></p><p id="ct5f6w_11">Complete wash without wash certificate</p></section><section class="chapter"><h2 id="authentication" data-toc="authentication">Authentication</h2><p id="ct5f6w_13">Security requirements:</p><div class="table-wrapper"><table class="wide" id="ct5f6w_14"><thead><tr class="ijRowHead" id="ct5f6w_15"><th id="ct5f6w_17"><p>Scheme</p></th><th id="ct5f6w_18"><p>Scopes</p></th></tr></thead><tbody><tr id="ct5f6w_16"><td id="ct5f6w_19"><p>BearerAuth</p></td><td id="ct5f6w_20"><p>-</p></td></tr></tbody></table></div></section><section class="chapter"><h2 id="request-body" data-toc="request-body">Request Body</h2><p id="ct5f6w_21">Required: yes.</p><p id="ct5f6w_22">Content type: <code class="code" id="ct5f6w_24">application/json</code></p><div class="code-block" data-lang="json">
{
&quot;properties&quot;: {
&quot;id&quot;: {
&quot;type&quot;: &quot;integer&quot;
}
},
&quot;required&quot;: [
&quot;id&quot;
],
&quot;type&quot;: &quot;object&quot;
}
</div></section><section class="chapter"><h2 id="responses" data-toc="responses">Responses</h2><div class="table-wrapper"><table class="wide" id="ct5f6w_25"><thead><tr class="ijRowHead" id="ct5f6w_28"><th id="ct5f6w_30"><p>Status</p></th><th id="ct5f6w_31"><p>Description</p></th><th id="ct5f6w_32"><p>Content Types</p></th></tr></thead><tbody><tr id="ct5f6w_29"><td id="ct5f6w_33"><p>200</p></td><td id="ct5f6w_34"><p>Success</p></td><td id="ct5f6w_35"><p>application/json</p></td></tr></tbody></table></div><p id="ct5f6w_26">Schema for response <code class="code" id="ct5f6w_36">200</code> (<code class="code" id="ct5f6w_37">application/json</code>):</p><div class="code-block" data-lang="json">
{}
</div></section><div class="last-modified">17 March 2026</div><div data-feedback-placeholder="true"></div><div class="navigation-links _bottom"><a href="tag-bookings-page-1.html" class="navigation-links__prev">Bookings - Page 1 of 1</a><a href="admindeletebooking.html" class="navigation-links__next">Delete booking (admin)</a></div></article><div id="disqus_thread"></div></div></section></main></div><script src="https://resources.jetbrains.com/writerside/apidoc/6.26.0-b913/app.js"></script></body></html>
@@ -0,0 +1,16 @@
<!DOCTYPE html SYSTEM "about:legacy-compat">
<html lang="en-US" data-preset="contrast" data-primary-color="#307FFF"><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8"><meta charset="UTF-8"><meta name="robots" content="noindex"><meta name="built-on" content="2026-03-17T15:45:47.011784678"><title>Backups - Page 1 of 1 | Copenhagen Truck Wash API</title><script type="application/json" id="virtual-toc-data">[]</script><script type="application/json" id="topic-shortcuts"></script><link href="https://resources.jetbrains.com/writerside/apidoc/6.26.0-b913/app.css" rel="stylesheet"><meta name="msapplication-TileColor" content="#000000"><link rel="apple-touch-icon" sizes="180x180" href="https://jetbrains.com/apple-touch-icon.png"><link rel="icon" type="image/png" sizes="32x32" href="https://jetbrains.com/favicon-32x32.png"><link rel="icon" type="image/png" sizes="16x16" href="https://jetbrains.com/favicon-16x16.png"><meta name="msapplication-TileImage" content="https://resources.jetbrains.com/storage/ui/favicons/mstile-144x144.png"><meta name="msapplication-square70x70logo" content="https://resources.jetbrains.com/storage/ui/favicons/mstile-70x70.png"><meta name="msapplication-square150x150logo" content="https://resources.jetbrains.com/storage/ui/favicons/mstile-150x150.png"><meta name="msapplication-wide310x150logo" content="https://resources.jetbrains.com/storage/ui/favicons/mstile-310x150.png"><meta name="msapplication-square310x310logo" content="https://resources.jetbrains.com/storage/ui/favicons/mstile-310x310.png"><meta name="image" content=""><!-- Open Graph --><meta property="og:title" content="Backups - Page 1 of 1 | Copenhagen Truck Wash API"><meta property="og:description" content=""><meta property="og:image" content=""><meta property="og:site_name" content="Copenhagen Truck Wash API Help"><meta property="og:type" content="website"><meta property="og:locale" content="en_US"><meta property="og:url" content="writerside-documentation/config-module-backups-page-1.html"><!-- End Open Graph --><!-- Twitter Card --><meta name="twitter:card" content="summary_large_image"><meta name="twitter:site" content=""><meta name="twitter:title" content="Backups - Page 1 of 1 | Copenhagen Truck Wash API"><meta name="twitter:description" content=""><meta name="twitter:creator" content=""><meta name="twitter:image:src" content=""><!-- End Twitter Card --><!-- Schema.org WebPage --><script type="application/ld+json">{
"@context": "http://schema.org",
"@type": "WebPage",
"@id": "writerside-documentation/config-module-backups-page-1.html#webpage",
"url": "writerside-documentation/config-module-backups-page-1.html",
"name": "Backups - Page 1 of 1 | Copenhagen Truck Wash API",
"description": "",
"image": "",
"inLanguage":"en-US"
}</script><!-- End Schema.org --><!-- Schema.org WebSite --><script type="application/ld+json">{
"@type": "WebSite",
"@id": "writerside-documentation/#website",
"url": "writerside-documentation/",
"name": "Copenhagen Truck Wash API Help"
}</script><!-- End Schema.org --></head><body data-id="config_module_backups_page_1" data-main-title="Backups - Page 1 of 1" data-article-props="{&quot;seeAlsoStyle&quot;:&quot;links&quot;}" data-template="article" data-breadcrumbs=""><div class="wrapper"><main class="panel _main"><header class="panel__header"><div class="container"><h3>Copenhagen Truck Wash API Help</h3><div class="panel-trigger"></div></div></header><section class="panel__content"><div class="container"><article class="article" data-shortcut-switcher="inactive"><h1 data-toc="config_module_backups_page_1" id="Config_Module_Backups_Page_1.topic">Backups - Page 1 of 1</h1><p id="z8q80g5_2">This page groups endpoint topics for this object type.</p><div class="last-modified">17 March 2026</div><div data-feedback-placeholder="true"></div><div class="navigation-links _bottom"><a href="modules-module-fxratesapi-page-1.html" class="navigation-links__prev">FXRatesAPI - Page 1 of 1</a><a href="modules-module-weatherapi.html" class="navigation-links__next">WeatherAPI</a></div></article><div id="disqus_thread"></div></div></section></main></div><script src="https://resources.jetbrains.com/writerside/apidoc/6.26.0-b913/app.js"></script></body></html>
@@ -0,0 +1,16 @@
<!DOCTYPE html SYSTEM "about:legacy-compat">
<html lang="en-US" data-preset="contrast" data-primary-color="#307FFF"><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8"><meta charset="UTF-8"><meta name="robots" content="noindex"><meta name="built-on" content="2026-03-17T15:42:14.678178928"><title>Backups | Copenhagen Truck Wash API</title><script type="application/json" id="virtual-toc-data">[]</script><script type="application/json" id="topic-shortcuts"></script><link href="https://resources.jetbrains.com/writerside/apidoc/6.26.0-b913/app.css" rel="stylesheet"><meta name="msapplication-TileColor" content="#000000"><link rel="apple-touch-icon" sizes="180x180" href="https://jetbrains.com/apple-touch-icon.png"><link rel="icon" type="image/png" sizes="32x32" href="https://jetbrains.com/favicon-32x32.png"><link rel="icon" type="image/png" sizes="16x16" href="https://jetbrains.com/favicon-16x16.png"><meta name="msapplication-TileImage" content="https://resources.jetbrains.com/storage/ui/favicons/mstile-144x144.png"><meta name="msapplication-square70x70logo" content="https://resources.jetbrains.com/storage/ui/favicons/mstile-70x70.png"><meta name="msapplication-square150x150logo" content="https://resources.jetbrains.com/storage/ui/favicons/mstile-150x150.png"><meta name="msapplication-wide310x150logo" content="https://resources.jetbrains.com/storage/ui/favicons/mstile-310x150.png"><meta name="msapplication-square310x310logo" content="https://resources.jetbrains.com/storage/ui/favicons/mstile-310x310.png"><meta name="image" content=""><!-- Open Graph --><meta property="og:title" content="Backups | Copenhagen Truck Wash API"><meta property="og:description" content=""><meta property="og:image" content=""><meta property="og:site_name" content="Copenhagen Truck Wash API Help"><meta property="og:type" content="website"><meta property="og:locale" content="en_US"><meta property="og:url" content="writerside-documentation/config-module-backups.html"><!-- End Open Graph --><!-- Twitter Card --><meta name="twitter:card" content="summary_large_image"><meta name="twitter:site" content=""><meta name="twitter:title" content="Backups | Copenhagen Truck Wash API"><meta name="twitter:description" content=""><meta name="twitter:creator" content=""><meta name="twitter:image:src" content=""><!-- End Twitter Card --><!-- Schema.org WebPage --><script type="application/ld+json">{
"@context": "http://schema.org",
"@type": "WebPage",
"@id": "writerside-documentation/config-module-backups.html#webpage",
"url": "writerside-documentation/config-module-backups.html",
"name": "Backups | Copenhagen Truck Wash API",
"description": "",
"image": "",
"inLanguage":"en-US"
}</script><!-- End Schema.org --><!-- Schema.org WebSite --><script type="application/ld+json">{
"@type": "WebSite",
"@id": "writerside-documentation/#website",
"url": "writerside-documentation/",
"name": "Copenhagen Truck Wash API Help"
}</script><!-- End Schema.org --></head><body data-id="config_module_backups" data-main-title="Backups" data-article-props="{&quot;seeAlsoStyle&quot;:&quot;links&quot;}" data-template="article" data-breadcrumbs=""><div class="wrapper"><main class="panel _main"><header class="panel__header"><div class="container"><h3>Copenhagen Truck Wash API Help</h3><div class="panel-trigger"></div></div></header><section class="panel__content"><div class="container"><article class="article" data-shortcut-switcher="inactive"><h1 data-toc="config_module_backups" id="Config_Module_Backups.topic">Backups</h1><p id="-8qe45f_2">Backup configuration for backup module behavior.</p><div class="last-modified">17 March 2026</div><div data-feedback-placeholder="true"></div><div class="navigation-links _bottom"><a href="config-module-stripe-page-1.html" class="navigation-links__prev">Stripe - Page 1 of 1</a><a href="config-module-e-conomic.html" class="navigation-links__next">e-conomic</a></div></article><div id="disqus_thread"></div></div></section></main></div><script src="https://resources.jetbrains.com/writerside/apidoc/6.26.0-b913/app.js"></script></body></html>
@@ -0,0 +1,16 @@
<!DOCTYPE html SYSTEM "about:legacy-compat">
<html lang="en-US" data-preset="contrast" data-primary-color="#307FFF"><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8"><meta charset="UTF-8"><meta name="robots" content="noindex"><meta name="built-on" content="2026-03-17T15:40:10.70963379"><title>Bird - Page 1 of 1 | Copenhagen Truck Wash API</title><script type="application/json" id="virtual-toc-data">[]</script><script type="application/json" id="topic-shortcuts"></script><link href="https://resources.jetbrains.com/writerside/apidoc/6.26.0-b913/app.css" rel="stylesheet"><meta name="msapplication-TileColor" content="#000000"><link rel="apple-touch-icon" sizes="180x180" href="https://jetbrains.com/apple-touch-icon.png"><link rel="icon" type="image/png" sizes="32x32" href="https://jetbrains.com/favicon-32x32.png"><link rel="icon" type="image/png" sizes="16x16" href="https://jetbrains.com/favicon-16x16.png"><meta name="msapplication-TileImage" content="https://resources.jetbrains.com/storage/ui/favicons/mstile-144x144.png"><meta name="msapplication-square70x70logo" content="https://resources.jetbrains.com/storage/ui/favicons/mstile-70x70.png"><meta name="msapplication-square150x150logo" content="https://resources.jetbrains.com/storage/ui/favicons/mstile-150x150.png"><meta name="msapplication-wide310x150logo" content="https://resources.jetbrains.com/storage/ui/favicons/mstile-310x150.png"><meta name="msapplication-square310x310logo" content="https://resources.jetbrains.com/storage/ui/favicons/mstile-310x310.png"><meta name="image" content=""><!-- Open Graph --><meta property="og:title" content="Bird - Page 1 of 1 | Copenhagen Truck Wash API"><meta property="og:description" content=""><meta property="og:image" content=""><meta property="og:site_name" content="Copenhagen Truck Wash API Help"><meta property="og:type" content="website"><meta property="og:locale" content="en_US"><meta property="og:url" content="writerside-documentation/config-module-bird-page-1.html"><!-- End Open Graph --><!-- Twitter Card --><meta name="twitter:card" content="summary_large_image"><meta name="twitter:site" content=""><meta name="twitter:title" content="Bird - Page 1 of 1 | Copenhagen Truck Wash API"><meta name="twitter:description" content=""><meta name="twitter:creator" content=""><meta name="twitter:image:src" content=""><!-- End Twitter Card --><!-- Schema.org WebPage --><script type="application/ld+json">{
"@context": "http://schema.org",
"@type": "WebPage",
"@id": "writerside-documentation/config-module-bird-page-1.html#webpage",
"url": "writerside-documentation/config-module-bird-page-1.html",
"name": "Bird - Page 1 of 1 | Copenhagen Truck Wash API",
"description": "",
"image": "",
"inLanguage":"en-US"
}</script><!-- End Schema.org --><!-- Schema.org WebSite --><script type="application/ld+json">{
"@type": "WebSite",
"@id": "writerside-documentation/#website",
"url": "writerside-documentation/",
"name": "Copenhagen Truck Wash API Help"
}</script><!-- End Schema.org --></head><body data-id="config_module_bird_page_1" data-main-title="Bird - Page 1 of 1" data-article-props="{&quot;seeAlsoStyle&quot;:&quot;links&quot;}" data-template="article" data-breadcrumbs=""><div class="wrapper"><main class="panel _main"><header class="panel__header"><div class="container"><h3>Copenhagen Truck Wash API Help</h3><div class="panel-trigger"></div></div></header><section class="panel__content"><div class="container"><article class="article" data-shortcut-switcher="inactive"><h1 data-toc="config_module_bird_page_1" id="Config_Module_Bird_Page_1.topic">Bird - Page 1 of 1</h1><p id="-8hrte5_2">This page groups endpoint topics for this object type.</p><div class="last-modified">17 March 2026</div><div data-feedback-placeholder="true"></div><div class="navigation-links _bottom"><a href="modules-module-backup.html" class="navigation-links__prev">Backup</a><a href="modules-module-cvr.html" class="navigation-links__next">CVR</a></div></article><div id="disqus_thread"></div></div></section></main></div><script src="https://resources.jetbrains.com/writerside/apidoc/6.26.0-b913/app.js"></script></body></html>
@@ -0,0 +1,16 @@
<!DOCTYPE html SYSTEM "about:legacy-compat">
<html lang="en-US" data-preset="contrast" data-primary-color="#307FFF"><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8"><meta charset="UTF-8"><meta name="robots" content="noindex"><meta name="built-on" content="2026-03-17T15:38:05.937163103"><title>Bird | Copenhagen Truck Wash API</title><script type="application/json" id="virtual-toc-data">[]</script><script type="application/json" id="topic-shortcuts"></script><link href="https://resources.jetbrains.com/writerside/apidoc/6.26.0-b913/app.css" rel="stylesheet"><meta name="msapplication-TileColor" content="#000000"><link rel="apple-touch-icon" sizes="180x180" href="https://jetbrains.com/apple-touch-icon.png"><link rel="icon" type="image/png" sizes="32x32" href="https://jetbrains.com/favicon-32x32.png"><link rel="icon" type="image/png" sizes="16x16" href="https://jetbrains.com/favicon-16x16.png"><meta name="msapplication-TileImage" content="https://resources.jetbrains.com/storage/ui/favicons/mstile-144x144.png"><meta name="msapplication-square70x70logo" content="https://resources.jetbrains.com/storage/ui/favicons/mstile-70x70.png"><meta name="msapplication-square150x150logo" content="https://resources.jetbrains.com/storage/ui/favicons/mstile-150x150.png"><meta name="msapplication-wide310x150logo" content="https://resources.jetbrains.com/storage/ui/favicons/mstile-310x150.png"><meta name="msapplication-square310x310logo" content="https://resources.jetbrains.com/storage/ui/favicons/mstile-310x310.png"><meta name="image" content=""><!-- Open Graph --><meta property="og:title" content="Bird | Copenhagen Truck Wash API"><meta property="og:description" content=""><meta property="og:image" content=""><meta property="og:site_name" content="Copenhagen Truck Wash API Help"><meta property="og:type" content="website"><meta property="og:locale" content="en_US"><meta property="og:url" content="writerside-documentation/config-module-bird.html"><!-- End Open Graph --><!-- Twitter Card --><meta name="twitter:card" content="summary_large_image"><meta name="twitter:site" content=""><meta name="twitter:title" content="Bird | Copenhagen Truck Wash API"><meta name="twitter:description" content=""><meta name="twitter:creator" content=""><meta name="twitter:image:src" content=""><!-- End Twitter Card --><!-- Schema.org WebPage --><script type="application/ld+json">{
"@context": "http://schema.org",
"@type": "WebPage",
"@id": "writerside-documentation/config-module-bird.html#webpage",
"url": "writerside-documentation/config-module-bird.html",
"name": "Bird | Copenhagen Truck Wash API",
"description": "",
"image": "",
"inLanguage":"en-US"
}</script><!-- End Schema.org --><!-- Schema.org WebSite --><script type="application/ld+json">{
"@type": "WebSite",
"@id": "writerside-documentation/#website",
"url": "writerside-documentation/",
"name": "Copenhagen Truck Wash API Help"
}</script><!-- End Schema.org --></head><body data-id="config_module_bird" data-main-title="Bird" data-article-props="{&quot;seeAlsoStyle&quot;:&quot;links&quot;}" data-template="article" data-breadcrumbs=""><div class="wrapper"><main class="panel _main"><header class="panel__header"><div class="container"><h3>Copenhagen Truck Wash API Help</h3><div class="panel-trigger"></div></div></header><section class="panel__content"><div class="container"><article class="article" data-shortcut-switcher="inactive"><h1 data-toc="config_module_bird" id="Config_Module_Bird.topic">Bird</h1><p id="z40nlsf_2">Bird communication integration configuration.</p><div class="last-modified">17 March 2026</div><div data-feedback-placeholder="true"></div><div class="navigation-links _bottom"><a href="modules-module-self-serve.html" class="navigation-links__prev">Self-Serve</a><a href="config-module-limble-page-1.html" class="navigation-links__next">Limble - Page 1 of 1</a></div></article><div id="disqus_thread"></div></div></section></main></div><script src="https://resources.jetbrains.com/writerside/apidoc/6.26.0-b913/app.js"></script></body></html>
@@ -0,0 +1,16 @@
<!DOCTYPE html SYSTEM "about:legacy-compat">
<html lang="en-US" data-preset="contrast" data-primary-color="#307FFF"><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8"><meta charset="UTF-8"><meta name="robots" content="noindex"><meta name="built-on" content="2026-03-17T15:45:45.048393176"><title>e-conomic - Page 1 of 1 | Copenhagen Truck Wash API</title><script type="application/json" id="virtual-toc-data">[]</script><script type="application/json" id="topic-shortcuts"></script><link href="https://resources.jetbrains.com/writerside/apidoc/6.26.0-b913/app.css" rel="stylesheet"><meta name="msapplication-TileColor" content="#000000"><link rel="apple-touch-icon" sizes="180x180" href="https://jetbrains.com/apple-touch-icon.png"><link rel="icon" type="image/png" sizes="32x32" href="https://jetbrains.com/favicon-32x32.png"><link rel="icon" type="image/png" sizes="16x16" href="https://jetbrains.com/favicon-16x16.png"><meta name="msapplication-TileImage" content="https://resources.jetbrains.com/storage/ui/favicons/mstile-144x144.png"><meta name="msapplication-square70x70logo" content="https://resources.jetbrains.com/storage/ui/favicons/mstile-70x70.png"><meta name="msapplication-square150x150logo" content="https://resources.jetbrains.com/storage/ui/favicons/mstile-150x150.png"><meta name="msapplication-wide310x150logo" content="https://resources.jetbrains.com/storage/ui/favicons/mstile-310x150.png"><meta name="msapplication-square310x310logo" content="https://resources.jetbrains.com/storage/ui/favicons/mstile-310x310.png"><meta name="image" content=""><!-- Open Graph --><meta property="og:title" content="e-conomic - Page 1 of 1 | Copenhagen Truck Wash API"><meta property="og:description" content=""><meta property="og:image" content=""><meta property="og:site_name" content="Copenhagen Truck Wash API Help"><meta property="og:type" content="website"><meta property="og:locale" content="en_US"><meta property="og:url" content="writerside-documentation/config-module-e-conomic-page-1.html"><!-- End Open Graph --><!-- Twitter Card --><meta name="twitter:card" content="summary_large_image"><meta name="twitter:site" content=""><meta name="twitter:title" content="e-conomic - Page 1 of 1 | Copenhagen Truck Wash API"><meta name="twitter:description" content=""><meta name="twitter:creator" content=""><meta name="twitter:image:src" content=""><!-- End Twitter Card --><!-- Schema.org WebPage --><script type="application/ld+json">{
"@context": "http://schema.org",
"@type": "WebPage",
"@id": "writerside-documentation/config-module-e-conomic-page-1.html#webpage",
"url": "writerside-documentation/config-module-e-conomic-page-1.html",
"name": "e-conomic - Page 1 of 1 | Copenhagen Truck Wash API",
"description": "",
"image": "",
"inLanguage":"en-US"
}</script><!-- End Schema.org --><!-- Schema.org WebSite --><script type="application/ld+json">{
"@type": "WebSite",
"@id": "writerside-documentation/#website",
"url": "writerside-documentation/",
"name": "Copenhagen Truck Wash API Help"
}</script><!-- End Schema.org --></head><body data-id="config_module_e_conomic_page_1" data-main-title="e-conomic - Page 1 of 1" data-article-props="{&quot;seeAlsoStyle&quot;:&quot;links&quot;}" data-template="article" data-breadcrumbs=""><div class="wrapper"><main class="panel _main"><header class="panel__header"><div class="container"><h3>Copenhagen Truck Wash API Help</h3><div class="panel-trigger"></div></div></header><section class="panel__content"><div class="container"><article class="article" data-shortcut-switcher="inactive"><h1 data-toc="config_module_e_conomic_page_1" id="Config_Module_e_conomic_Page_1.topic">e-conomic - Page 1 of 1</h1><p id="q6uyvq_2">This page groups endpoint topics for this object type.</p><div class="last-modified">17 March 2026</div><div data-feedback-placeholder="true"></div><div class="navigation-links _bottom"><a href="config-module-virkdata-page-1.html" class="navigation-links__prev">VirkData - Page 1 of 1</a><a href="config-module-virkdata.html" class="navigation-links__next">VirkData</a></div></article><div id="disqus_thread"></div></div></section></main></div><script src="https://resources.jetbrains.com/writerside/apidoc/6.26.0-b913/app.js"></script></body></html>
@@ -0,0 +1,16 @@
<!DOCTYPE html SYSTEM "about:legacy-compat">
<html lang="en-US" data-preset="contrast" data-primary-color="#307FFF"><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8"><meta charset="UTF-8"><meta name="robots" content="noindex"><meta name="built-on" content="2026-03-17T15:42:26.640595286"><title>e-conomic | Copenhagen Truck Wash API</title><script type="application/json" id="virtual-toc-data">[]</script><script type="application/json" id="topic-shortcuts"></script><link href="https://resources.jetbrains.com/writerside/apidoc/6.26.0-b913/app.css" rel="stylesheet"><meta name="msapplication-TileColor" content="#000000"><link rel="apple-touch-icon" sizes="180x180" href="https://jetbrains.com/apple-touch-icon.png"><link rel="icon" type="image/png" sizes="32x32" href="https://jetbrains.com/favicon-32x32.png"><link rel="icon" type="image/png" sizes="16x16" href="https://jetbrains.com/favicon-16x16.png"><meta name="msapplication-TileImage" content="https://resources.jetbrains.com/storage/ui/favicons/mstile-144x144.png"><meta name="msapplication-square70x70logo" content="https://resources.jetbrains.com/storage/ui/favicons/mstile-70x70.png"><meta name="msapplication-square150x150logo" content="https://resources.jetbrains.com/storage/ui/favicons/mstile-150x150.png"><meta name="msapplication-wide310x150logo" content="https://resources.jetbrains.com/storage/ui/favicons/mstile-310x150.png"><meta name="msapplication-square310x310logo" content="https://resources.jetbrains.com/storage/ui/favicons/mstile-310x310.png"><meta name="image" content=""><!-- Open Graph --><meta property="og:title" content="e-conomic | Copenhagen Truck Wash API"><meta property="og:description" content=""><meta property="og:image" content=""><meta property="og:site_name" content="Copenhagen Truck Wash API Help"><meta property="og:type" content="website"><meta property="og:locale" content="en_US"><meta property="og:url" content="writerside-documentation/config-module-e-conomic.html"><!-- End Open Graph --><!-- Twitter Card --><meta name="twitter:card" content="summary_large_image"><meta name="twitter:site" content=""><meta name="twitter:title" content="e-conomic | Copenhagen Truck Wash API"><meta name="twitter:description" content=""><meta name="twitter:creator" content=""><meta name="twitter:image:src" content=""><!-- End Twitter Card --><!-- Schema.org WebPage --><script type="application/ld+json">{
"@context": "http://schema.org",
"@type": "WebPage",
"@id": "writerside-documentation/config-module-e-conomic.html#webpage",
"url": "writerside-documentation/config-module-e-conomic.html",
"name": "e-conomic | Copenhagen Truck Wash API",
"description": "",
"image": "",
"inLanguage":"en-US"
}</script><!-- End Schema.org --><!-- Schema.org WebSite --><script type="application/ld+json">{
"@type": "WebSite",
"@id": "writerside-documentation/#website",
"url": "writerside-documentation/",
"name": "Copenhagen Truck Wash API Help"
}</script><!-- End Schema.org --></head><body data-id="config_module_e_conomic" data-main-title="e-conomic" data-article-props="{&quot;seeAlsoStyle&quot;:&quot;links&quot;}" data-template="article" data-breadcrumbs=""><div class="wrapper"><main class="panel _main"><header class="panel__header"><div class="container"><h3>Copenhagen Truck Wash API Help</h3><div class="panel-trigger"></div></div></header><section class="panel__content"><div class="container"><article class="article" data-shortcut-switcher="inactive"><h1 data-toc="config_module_e_conomic" id="Config_Module_e_conomic.topic">e-conomic</h1><p id="-17q2b8_2">e-conomic accounting integration configuration.</p><div class="last-modified">17 March 2026</div><div data-feedback-placeholder="true"></div><div class="navigation-links _bottom"><a href="config-module-backups.html" class="navigation-links__prev">Backups</a><a href="config-module-limble.html" class="navigation-links__next">Limble</a></div></article><div id="disqus_thread"></div></div></section></main></div><script src="https://resources.jetbrains.com/writerside/apidoc/6.26.0-b913/app.js"></script></body></html>
@@ -0,0 +1,16 @@
<!DOCTYPE html SYSTEM "about:legacy-compat">
<html lang="en-US" data-preset="contrast" data-primary-color="#307FFF"><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8"><meta charset="UTF-8"><meta name="robots" content="noindex"><meta name="built-on" content="2026-03-17T15:44:23.581122823"><title>Email - Page 1 of 1 | Copenhagen Truck Wash API</title><script type="application/json" id="virtual-toc-data">[]</script><script type="application/json" id="topic-shortcuts"></script><link href="https://resources.jetbrains.com/writerside/apidoc/6.26.0-b913/app.css" rel="stylesheet"><meta name="msapplication-TileColor" content="#000000"><link rel="apple-touch-icon" sizes="180x180" href="https://jetbrains.com/apple-touch-icon.png"><link rel="icon" type="image/png" sizes="32x32" href="https://jetbrains.com/favicon-32x32.png"><link rel="icon" type="image/png" sizes="16x16" href="https://jetbrains.com/favicon-16x16.png"><meta name="msapplication-TileImage" content="https://resources.jetbrains.com/storage/ui/favicons/mstile-144x144.png"><meta name="msapplication-square70x70logo" content="https://resources.jetbrains.com/storage/ui/favicons/mstile-70x70.png"><meta name="msapplication-square150x150logo" content="https://resources.jetbrains.com/storage/ui/favicons/mstile-150x150.png"><meta name="msapplication-wide310x150logo" content="https://resources.jetbrains.com/storage/ui/favicons/mstile-310x150.png"><meta name="msapplication-square310x310logo" content="https://resources.jetbrains.com/storage/ui/favicons/mstile-310x310.png"><meta name="image" content=""><!-- Open Graph --><meta property="og:title" content="Email - Page 1 of 1 | Copenhagen Truck Wash API"><meta property="og:description" content=""><meta property="og:image" content=""><meta property="og:site_name" content="Copenhagen Truck Wash API Help"><meta property="og:type" content="website"><meta property="og:locale" content="en_US"><meta property="og:url" content="writerside-documentation/config-module-email-page-1.html"><!-- End Open Graph --><!-- Twitter Card --><meta name="twitter:card" content="summary_large_image"><meta name="twitter:site" content=""><meta name="twitter:title" content="Email - Page 1 of 1 | Copenhagen Truck Wash API"><meta name="twitter:description" content=""><meta name="twitter:creator" content=""><meta name="twitter:image:src" content=""><!-- End Twitter Card --><!-- Schema.org WebPage --><script type="application/ld+json">{
"@context": "http://schema.org",
"@type": "WebPage",
"@id": "writerside-documentation/config-module-email-page-1.html#webpage",
"url": "writerside-documentation/config-module-email-page-1.html",
"name": "Email - Page 1 of 1 | Copenhagen Truck Wash API",
"description": "",
"image": "",
"inLanguage":"en-US"
}</script><!-- End Schema.org --><!-- Schema.org WebSite --><script type="application/ld+json">{
"@type": "WebSite",
"@id": "writerside-documentation/#website",
"url": "writerside-documentation/",
"name": "Copenhagen Truck Wash API Help"
}</script><!-- End Schema.org --></head><body data-id="config_module_email_page_1" data-main-title="Email - Page 1 of 1" data-article-props="{&quot;seeAlsoStyle&quot;:&quot;links&quot;}" data-template="article" data-breadcrumbs=""><div class="wrapper"><main class="panel _main"><header class="panel__header"><div class="container"><h3>Copenhagen Truck Wash API Help</h3><div class="panel-trigger"></div></div></header><section class="panel__content"><div class="container"><article class="article" data-shortcut-switcher="inactive"><h1 data-toc="config_module_email_page_1" id="Config_Module_Email_Page_1.topic">Email - Page 1 of 1</h1><p id="jylghm_2">This page groups endpoint topics for this object type.</p><div class="last-modified">17 March 2026</div><div data-feedback-placeholder="true"></div><div class="navigation-links _bottom"><a href="modules-module-motorapi-page-1.html" class="navigation-links__prev">MotorAPI - Page 1 of 1</a><a href="modules-module-virkdata-page-1.html" class="navigation-links__next">VirkData - Page 1 of 1</a></div></article><div id="disqus_thread"></div></div></section></main></div><script src="https://resources.jetbrains.com/writerside/apidoc/6.26.0-b913/app.js"></script></body></html>
@@ -0,0 +1,16 @@
<!DOCTYPE html SYSTEM "about:legacy-compat">
<html lang="en-US" data-preset="contrast" data-primary-color="#307FFF"><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8"><meta charset="UTF-8"><meta name="robots" content="noindex"><meta name="built-on" content="2026-03-17T15:45:14.939458395"><title>Email | Copenhagen Truck Wash API</title><script type="application/json" id="virtual-toc-data">[]</script><script type="application/json" id="topic-shortcuts"></script><link href="https://resources.jetbrains.com/writerside/apidoc/6.26.0-b913/app.css" rel="stylesheet"><meta name="msapplication-TileColor" content="#000000"><link rel="apple-touch-icon" sizes="180x180" href="https://jetbrains.com/apple-touch-icon.png"><link rel="icon" type="image/png" sizes="32x32" href="https://jetbrains.com/favicon-32x32.png"><link rel="icon" type="image/png" sizes="16x16" href="https://jetbrains.com/favicon-16x16.png"><meta name="msapplication-TileImage" content="https://resources.jetbrains.com/storage/ui/favicons/mstile-144x144.png"><meta name="msapplication-square70x70logo" content="https://resources.jetbrains.com/storage/ui/favicons/mstile-70x70.png"><meta name="msapplication-square150x150logo" content="https://resources.jetbrains.com/storage/ui/favicons/mstile-150x150.png"><meta name="msapplication-wide310x150logo" content="https://resources.jetbrains.com/storage/ui/favicons/mstile-310x150.png"><meta name="msapplication-square310x310logo" content="https://resources.jetbrains.com/storage/ui/favicons/mstile-310x310.png"><meta name="image" content=""><!-- Open Graph --><meta property="og:title" content="Email | Copenhagen Truck Wash API"><meta property="og:description" content=""><meta property="og:image" content=""><meta property="og:site_name" content="Copenhagen Truck Wash API Help"><meta property="og:type" content="website"><meta property="og:locale" content="en_US"><meta property="og:url" content="writerside-documentation/config-module-email.html"><!-- End Open Graph --><!-- Twitter Card --><meta name="twitter:card" content="summary_large_image"><meta name="twitter:site" content=""><meta name="twitter:title" content="Email | Copenhagen Truck Wash API"><meta name="twitter:description" content=""><meta name="twitter:creator" content=""><meta name="twitter:image:src" content=""><!-- End Twitter Card --><!-- Schema.org WebPage --><script type="application/ld+json">{
"@context": "http://schema.org",
"@type": "WebPage",
"@id": "writerside-documentation/config-module-email.html#webpage",
"url": "writerside-documentation/config-module-email.html",
"name": "Email | Copenhagen Truck Wash API",
"description": "",
"image": "",
"inLanguage":"en-US"
}</script><!-- End Schema.org --><!-- Schema.org WebSite --><script type="application/ld+json">{
"@type": "WebSite",
"@id": "writerside-documentation/#website",
"url": "writerside-documentation/",
"name": "Copenhagen Truck Wash API Help"
}</script><!-- End Schema.org --></head><body data-id="config_module_email" data-main-title="Email" data-article-props="{&quot;seeAlsoStyle&quot;:&quot;links&quot;}" data-template="article" data-breadcrumbs=""><div class="wrapper"><main class="panel _main"><header class="panel__header"><div class="container"><h3>Copenhagen Truck Wash API Help</h3><div class="panel-trigger"></div></div></header><section class="panel__content"><div class="container"><article class="article" data-shortcut-switcher="inactive"><h1 data-toc="config_module_email" id="Config_Module_Email.topic">Email</h1><p id="-b1iqfs_2">Email provider and SMTP/MailerSend configuration.</p><div class="last-modified">17 March 2026</div><div data-feedback-placeholder="true"></div><div class="navigation-links _bottom"><a href="modules-module-virkdata-page-1.html" class="navigation-links__prev">VirkData - Page 1 of 1</a><a href="modules-module-fxratesapi-page-1.html" class="navigation-links__next">FXRatesAPI - Page 1 of 1</a></div></article><div id="disqus_thread"></div></div></section></main></div><script src="https://resources.jetbrains.com/writerside/apidoc/6.26.0-b913/app.js"></script></body></html>
@@ -0,0 +1,16 @@
<!DOCTYPE html SYSTEM "about:legacy-compat">
<html lang="en-US" data-preset="contrast" data-primary-color="#307FFF"><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8"><meta charset="UTF-8"><meta name="robots" content="noindex"><meta name="built-on" content="2026-03-17T15:44:22.768536841"><title>Entra - Page 1 of 1 | Copenhagen Truck Wash API</title><script type="application/json" id="virtual-toc-data">[]</script><script type="application/json" id="topic-shortcuts"></script><link href="https://resources.jetbrains.com/writerside/apidoc/6.26.0-b913/app.css" rel="stylesheet"><meta name="msapplication-TileColor" content="#000000"><link rel="apple-touch-icon" sizes="180x180" href="https://jetbrains.com/apple-touch-icon.png"><link rel="icon" type="image/png" sizes="32x32" href="https://jetbrains.com/favicon-32x32.png"><link rel="icon" type="image/png" sizes="16x16" href="https://jetbrains.com/favicon-16x16.png"><meta name="msapplication-TileImage" content="https://resources.jetbrains.com/storage/ui/favicons/mstile-144x144.png"><meta name="msapplication-square70x70logo" content="https://resources.jetbrains.com/storage/ui/favicons/mstile-70x70.png"><meta name="msapplication-square150x150logo" content="https://resources.jetbrains.com/storage/ui/favicons/mstile-150x150.png"><meta name="msapplication-wide310x150logo" content="https://resources.jetbrains.com/storage/ui/favicons/mstile-310x150.png"><meta name="msapplication-square310x310logo" content="https://resources.jetbrains.com/storage/ui/favicons/mstile-310x310.png"><meta name="image" content=""><!-- Open Graph --><meta property="og:title" content="Entra - Page 1 of 1 | Copenhagen Truck Wash API"><meta property="og:description" content=""><meta property="og:image" content=""><meta property="og:site_name" content="Copenhagen Truck Wash API Help"><meta property="og:type" content="website"><meta property="og:locale" content="en_US"><meta property="og:url" content="writerside-documentation/config-module-entra-page-1.html"><!-- End Open Graph --><!-- Twitter Card --><meta name="twitter:card" content="summary_large_image"><meta name="twitter:site" content=""><meta name="twitter:title" content="Entra - Page 1 of 1 | Copenhagen Truck Wash API"><meta name="twitter:description" content=""><meta name="twitter:creator" content=""><meta name="twitter:image:src" content=""><!-- End Twitter Card --><!-- Schema.org WebPage --><script type="application/ld+json">{
"@context": "http://schema.org",
"@type": "WebPage",
"@id": "writerside-documentation/config-module-entra-page-1.html#webpage",
"url": "writerside-documentation/config-module-entra-page-1.html",
"name": "Entra - Page 1 of 1 | Copenhagen Truck Wash API",
"description": "",
"image": "",
"inLanguage":"en-US"
}</script><!-- End Schema.org --><!-- Schema.org WebSite --><script type="application/ld+json">{
"@type": "WebSite",
"@id": "writerside-documentation/#website",
"url": "writerside-documentation/",
"name": "Copenhagen Truck Wash API Help"
}</script><!-- End Schema.org --></head><body data-id="config_module_entra_page_1" data-main-title="Entra - Page 1 of 1" data-article-props="{&quot;seeAlsoStyle&quot;:&quot;links&quot;}" data-template="article" data-breadcrumbs=""><div class="wrapper"><main class="panel _main"><header class="panel__header"><div class="container"><h3>Copenhagen Truck Wash API Help</h3><div class="panel-trigger"></div></div></header><section class="panel__content"><div class="container"><article class="article" data-shortcut-switcher="inactive"><h1 data-toc="config_module_entra_page_1" id="Config_Module_Entra_Page_1.topic">Entra - Page 1 of 1</h1><p id="-et97ic_2">This page groups endpoint topics for this object type.</p><div class="last-modified">17 March 2026</div><div data-feedback-placeholder="true"></div><div class="navigation-links _bottom"><a href="config-module-gatewayapi-page-1.html" class="navigation-links__prev">GatewayAPI - Page 1 of 1</a><a href="config-module-self-serve-page-1.html" class="navigation-links__next">Self-Serve - Page 1 of 1</a></div></article><div id="disqus_thread"></div></div></section></main></div><script src="https://resources.jetbrains.com/writerside/apidoc/6.26.0-b913/app.js"></script></body></html>

Some files were not shown because too many files have changed in this diff Show More