diff --git a/docs/integrations/core-events.md b/docs/integrations/core-events.md
index 02edf5b5..6df4e83f 100644
--- a/docs/integrations/core-events.md
+++ b/docs/integrations/core-events.md
@@ -36,6 +36,8 @@ All events include these fields:
### Customer
+
+
### Billing Account
@@ -76,4 +78,4 @@ These events are triggered manually via automation.
-
\ No newline at end of file
+
diff --git a/docs/integrations/integration-toolkit/configuration.md b/docs/integrations/integration-toolkit/configuration.md
index 500764b2..6927f961 100644
--- a/docs/integrations/integration-toolkit/configuration.md
+++ b/docs/integrations/integration-toolkit/configuration.md
@@ -122,7 +122,7 @@ curl -X POST 'https://erp-integration.sls.epilot.io/v1/integrations/{integration
| `configuration` | object | Yes | Type-specific configuration (see sections below) |
:::info
-- `file_proxy` — On-demand file serving from external document systems. See the [File Proxy guide](./file-proxy.md).
+- `file_proxy` — On-demand file serving from external systems or outbound delivery of epilot files. See the [File Proxy](./file-proxy.md) and [Outbound File Delivery](./outbound-file-delivery.md) guides.
- `managed_call` — Synchronous external API calls with JSONata mapping. See [Managed Call Use Cases](#managed-call-use-cases).
- `secure_proxy` — Route requests through epilot's secure proxy for static IP or VPN access. See [Secure Proxy Use Cases](#secure-proxy-use-cases).
:::
@@ -181,11 +181,11 @@ curl -X POST 'https://erp-integration.sls.epilot.io/v1/integrations/{integration
| Property | Type | Required | Description |
|----------|------|----------|-------------|
-| `id` | string (UUID) | Yes | Unique identifier for the mapping |
+| `id` | string (UUID) | No | Unique identifier for the mapping; generated when omitted |
| `name` | string | Yes | Display name for the mapping |
| `enabled` | boolean | Yes | Whether this mapping is active |
-| `jsonata_expression` | string | For `webhook` delivery | JSONata expression to transform the event payload. Required for `webhook` delivery; ignored for `poll` delivery |
-| `delivery` | object | Yes | How the event is delivered — discriminated on `type`: `webhook` or `poll` |
+| `jsonata_expression` | string | For `webhook` delivery | JSONata expression to transform the event payload. Required for `webhook`, ignored for `poll`, and rejected for `file_proxy` delivery |
+| `delivery` | object | Yes | How the event is delivered — discriminated on `type`: `webhook`, `poll`, or `file_proxy` |
#### Delivery Types
@@ -228,6 +228,19 @@ curl -X POST 'https://erp-integration.sls.epilot.io/v1/integrations/{integration
Everything beyond the configuration contract — the polling and acknowledgement API, lease and ordering semantics, retention and expiry behavior, the dead-letter queue and operator actions, and poll-mode monitoring — is documented on the dedicated [Pollable Outbound](./pollable-outbound.md) page.
+**File proxy delivery (push):** points to an upload-direction `file_proxy` use case in the same integration. The referenced recipe owns fan-out, payload mapping, authentication, and HTTP steps. `jsonata_expression` is rejected on this mapping type, and the use case's `event_catalog_event` must declare `event_attachments`. See [Outbound File Delivery](./outbound-file-delivery.md) for the complete setup and runtime behavior.
+
+```json
+"delivery": {
+ "type": "file_proxy",
+ "use_case_slug": "customer-request-document-upload"
+}
+```
+
+| Property | Type | Required | Description |
+| --- | --- | --- | --- |
+| `use_case_slug` | string | Yes | Slug of an upload-direction `file_proxy` use case in the same integration |
+
## Mapping Configuration Schema
### Version 2.0 Structure
diff --git a/docs/integrations/integration-toolkit/external-monitoring-events.md b/docs/integrations/integration-toolkit/external-monitoring-events.md
index 5f25fef6..7214abf9 100644
--- a/docs/integrations/integration-toolkit/external-monitoring-events.md
+++ b/docs/integrations/integration-toolkit/external-monitoring-events.md
@@ -1,5 +1,5 @@
---
-sidebar_position: 7
+sidebar_position: 8
title: External Monitoring Events
description: Push monitoring events from an external system (e.g. an integration middleware) into epilot so the Integration Hub is your central monitoring point, with a cross-system event trace
slug: /integrations/integration-toolkit/external-monitoring-events
diff --git a/docs/integrations/integration-toolkit/file-proxy.md b/docs/integrations/integration-toolkit/file-proxy.md
index bdb30367..83566c9d 100644
--- a/docs/integrations/integration-toolkit/file-proxy.md
+++ b/docs/integrations/integration-toolkit/file-proxy.md
@@ -7,6 +7,10 @@ slug: /integrations/integration-toolkit/file-proxy
# File Proxy
+:::info File direction
+This page covers files moving **into epilot on demand** from an external archive. To deliver files referenced by epilot events to an external API, see [Outbound File Delivery](./outbound-file-delivery.md).
+:::
+
The File Proxy enables epilot to serve files from external document systems (e.g., ERP document archives, document management systems) **on demand**, without migrating files into epilot's storage. During [inbound sync](./inbound/getting-started.md), file entities are created with a `custom_download_url` pointing to the file proxy. When a user views the file, epilot's file service verifies the request and the proxy fetches the document from the external system in real time.
Each file proxy configuration is stored as a **use case** with `type: 'file_proxy'` within an integration. The configuration describes how to authenticate, which HTTP calls to make, and how to extract the file from the response — all declaratively, without code changes for new integrations.
diff --git a/docs/integrations/integration-toolkit/outbound-file-delivery.md b/docs/integrations/integration-toolkit/outbound-file-delivery.md
new file mode 100644
index 00000000..424224a9
--- /dev/null
+++ b/docs/integrations/integration-toolkit/outbound-file-delivery.md
@@ -0,0 +1,390 @@
+---
+sidebar_position: 7
+title: Outbound File Delivery
+description: Deliver files referenced by epilot events to an external API with fan-out, mapping, retries, and monitoring
+slug: /integrations/integration-toolkit/outbound-file-delivery
+---
+
+# Outbound File Delivery
+
+Outbound file delivery sends files referenced by an epilot event to an external document API. The Integration Toolkit fetches each file from epilot, maps the event and the file into the target API's format, and runs a declarative HTTP workflow.
+
+`CustomerRequestSubmitted` is the main example: one submitted request can contain several `event_attachments`, and with fan-out enabled each attachment is delivered and monitored independently.
+
+:::caution Trigger after attachments are ready
+
+`CustomerRequestSubmitted` is triggered explicitly by Automation; ticket creation no longer emits it automatically. Place the trigger action after the workflow has created the ticket and written its file relations. Event Catalog then hydrates the ticket's current graph and builds `event_attachments` from every related file.
+
+An empty attachment array is valid because a customer request may contain no uploads. If Automation triggers the event before the file relations are written, that empty snapshot is published and a later relation update does not replay it automatically.
+
+Without an attachment-count `event_filter`, an event with no attachments records `FAN_OUT_EMPTY`. With the example `$count(event_attachments) > 0` filter below, the use case is filtered out before fan-out, so no delivery is enqueued and no `FAN_OUT_EMPTY` is emitted for that mapping.
+
+For strict upload ordering, make the `CustomerRequestSubmitted` trigger action depend on the relation-writing step. If that workflow cannot provide the ordering, use a `FileUpdated` handoff or another dedicated event after the relations are complete.
+
+:::
+
+:::info Upload versus download
+This page covers files moving **out of epilot**. To serve files from an external archive when a user opens them in epilot, use the [download File Proxy](./file-proxy.md).
+:::
+
+## How it works
+
+The setup uses two use cases in the same integration:
+
+| Use case | Decides | Configuration |
+| --- | --- | --- |
+| `outbound` | **When** to deliver | Event Catalog event, optional event filter, and a pointer to the upload recipe |
+| `file_proxy` with `direction: "upload"` | **What and how** to deliver | Fan-out, mapping, authentication, HTTP steps, and limits |
+
+```mermaid
+sequenceDiagram
+ participant Event as Event Catalog
+ participant Outbound as Outbound delivery
+ participant Queue as File delivery queue
+ participant File as epilot File API
+ participant Target as External document API
+
+ Event->>Outbound: CustomerRequestSubmitted
+ Outbound->>Outbound: Apply event_filter and fan_out
+ loop Each event attachment
+ Outbound->>Queue: Enqueue durable delivery
+ Queue->>File: Fetch entity_id + version_index
+ File-->>Queue: File bytes and metadata
+ Queue->>Queue: Evaluate enabled and body_jsonata per step
+ Queue->>Target: Execute configured HTTP steps
+ Target-->>Queue: Result
+ end
+```
+
+The outbound delivery is a pure pointer to the file-proxy use-case slug. The recipe is resolved at runtime, so you can create the two use cases in either order. A missing, disabled, or wrong-direction target is reported through monitoring when the event is handled.
+
+## Before you start
+
+You need:
+
+- an Integration Toolkit integration;
+- an Event Catalog event that declares `event_attachments`, such as [`CustomerRequestSubmitted`](/docs/integrations/core-events#customer);
+- an external HTTP endpoint that accepts the file; and
+- environment values for target URLs and credentials.
+
+The examples use the Integration Toolkit API at `https://integration-toolkit.sls.epilot.io`.
+
+## 1. Create the upload recipe
+
+Create a `file_proxy` use case with `direction: "upload"`:
+
+```bash
+curl -X POST 'https://integration-toolkit.sls.epilot.io/v1/integrations/{integrationId}/use-cases' \
+ -H 'Authorization: Bearer ' \
+ -H 'Content-Type: application/json' \
+ -d '{
+ "name": "Customer request document upload",
+ "slug": "customer-request-document-upload",
+ "type": "file_proxy",
+ "enabled": true,
+ "configuration": {
+ "direction": "upload",
+ "fan_out": {
+ "enabled": true
+ },
+ "auth": {
+ "type": "oauth2_client_credentials",
+ "token_url": "{{env.DOCUMENT_TOKEN_URL}}",
+ "client_id": "{{env.DOCUMENT_CLIENT_ID}}",
+ "client_secret": "{{env.DOCUMENT_CLIENT_SECRET}}"
+ },
+ "steps": [
+ {
+ "method": "POST",
+ "url": "{{env.DOCUMENT_API_URL}}/documents",
+ "headers": {
+ "Content-Type": "application/json"
+ },
+ "body_jsonata": "{ \"customerNumber\": contact.customer_number, \"filename\": $file_data[0].filename, \"mimeType\": $file_data[0].mime_type, \"fileData\": $file_data[0].base64, \"sourceSystem\": \"epilot\", \"idempotencyKey\": $file_data[0].entity_id & \":\" & _event_id, \"submittedAt\": $germanDate($now()) }",
+ "response_type": "json"
+ }
+ ],
+ "upload": {
+ "max_file_bytes": 26214400,
+ "max_delivery_attempts": 8
+ }
+ }
+ }'
+```
+
+### Upload configuration
+
+| Field | Required | Description |
+| --- | --- | --- |
+| `direction` | Yes | Must be `upload`. Omitting it means the existing download behavior. |
+| `steps` | Yes | One or more HTTP requests. Upload steps support `GET`, `POST`, `PUT`, and `PATCH`. |
+| `upload` | Yes | Size ceilings and the retry limit. |
+| `fan_out` | No | Whether one event produces one delivery per file or a single delivery carrying all of them. |
+| `auth` | No | OAuth2 client credentials or password authentication. |
+| `secure_proxy` | No | Routes the steps through a `secure_proxy` use case in the same integration. |
+
+Each step is configured with:
+
+| Field | Required | Description |
+| --- | --- | --- |
+| `url` | Yes | Handlebars template for the request URL. |
+| `method` | Yes | `GET`, `POST`, `PUT`, or `PATCH`. |
+| `response_type` | Yes | `json` or `binary`. |
+| `headers` | No | Object whose values are Handlebars templates. |
+| `body_jsonata` | No | JSONata producing the request body as data. Leave it out to send the delivery's files unchanged. |
+| `enabled` | No | JSONata returning a boolean that decides whether this step runs. Absent means it runs. |
+
+Download-only fields are rejected for upload recipes: `params`, `response`, `allowed_origins`, `prevent_indirect_serving`, and a Handlebars `body` on a step.
+
+Fields that earlier versions of this feature carried are rejected as well, each naming its replacement: `params_mapping`, `required_params`, `constants`, `lookups`, `shared`, `file_source`, `fan_out.split_expression`, and a step's `required_keys` or `body_source`.
+
+`upload.success_when` and `upload.external_id` have also been removed. For compatibility they are not rejected, but they have no effect: a final response below `400` completes the workflow, and no external ID is stored or added to monitoring. Remove both fields from existing recipes.
+
+## 2. Subscribe to the event
+
+Create an outbound use case whose delivery points to the upload recipe's slug:
+
+```bash
+curl -X POST 'https://integration-toolkit.sls.epilot.io/v1/integrations/{integrationId}/use-cases' \
+ -H 'Authorization: Bearer ' \
+ -H 'Content-Type: application/json' \
+ -d '{
+ "name": "Deliver customer request documents",
+ "slug": "deliver-customer-request-documents",
+ "type": "outbound",
+ "enabled": true,
+ "configuration": {
+ "event_catalog_event": "CustomerRequestSubmitted",
+ "event_filter": "$count(event_attachments) > 0",
+ "ack_tracking": "off",
+ "mappings": [
+ {
+ "name": "External document archive",
+ "enabled": true,
+ "delivery": {
+ "type": "file_proxy",
+ "use_case_slug": "customer-request-document-upload"
+ }
+ }
+ ]
+ }
+ }'
+```
+
+The event's catalog schema must declare `event_attachments`. An outbound use case with a `file_proxy` mapping is rejected with `400` when that field is absent from the schema — the message names the event and the offending mapping — because such a configuration would fan out to nothing on every event it ever sees and report no error anywhere. An individual event may still carry an empty array. If the event catalog cannot be reached to check the schema, the save fails with a retryable `503` rather than being let through.
+
+Do not add `jsonata_expression` to a `file_proxy` outbound mapping. The referenced recipe owns payload mapping, and the API rejects an expression that would otherwise be ignored. Mapping IDs and timestamps are generated when omitted.
+
+The delivery object is now only `{ "type": "file_proxy", "use_case_slug": "..." }`. Legacy delivery fields are rejected: `attachment_selector` has no replacement because the split is fixed to `event_attachments`; `constants` and `lookups` belong inline in a step's `body_jsonata`; and `max_delivery_attempts` belongs at `upload.max_delivery_attempts` on the referenced recipe.
+
+Use `ack_tracking: "off"` for a file-only outbound use case: durable delivery state is tracked per file. If a webhook mapping in the same outbound use case requires acknowledgements, keep acknowledgement tracking enabled or separate the webhook and file deliveries.
+
+## Fan-out
+
+`fan_out.enabled` is the only decision. The split is always over the event's `event_attachments`, so there is no expression to write — an upload only ever runs on events that declare that field.
+
+```json
+"fan_out": {
+ "enabled": true
+}
+```
+
+- **`enabled: true`** — one delivery per attachment. Each is fully independent: its own idempotency record, its own retry schedule, its own monitoring events. A four-file event can therefore end up three-of-four delivered, which is the honest state to report.
+- **`enabled: false`** (or omitted) — the event produces exactly one delivery carrying every attachment.
+
+The split is evaluated once, when the event is enqueued, so item indices — and therefore idempotency keys — stay stable across retries.
+
+An event carrying no attachments produces no deliveries and records `FAN_OUT_EMPTY` at info level, which is the normal outcome for a catch-all subscription seeing an event with nothing to send. Use `event_filter` to decide whether the event should be handled at all.
+
+## Mapping the request body
+
+A step's `body_jsonata` produces the request body as **data**; the result is serialized to JSON and sent. Use it for every JSON body: it cannot emit malformed JSON, and it omits a key whose value is undefined instead of sending it empty, which is what makes optional fields work without a conditional guard.
+
+The evaluation root is the event, so `contact.customer_number`, `ticket._purpose` and `_event_id` are reachable directly, unprefixed. Everything else is a `$`-prefixed binding:
+
+| JSONata value | Contains |
+| --- | --- |
+| `$file_data` | The files this delivery carries, always an array |
+| `$steps` | Results of the steps already executed, each `{statusCode, headers, body}` |
+| `$ack_id` | Triggering event acknowledgement ID, when present |
+| `$env` | Organization environment variables |
+| `$now()` | Current ISO timestamp, one value for the whole delivery |
+| `$germanDate(iso)` | An ISO timestamp formatted as a German date |
+
+`$file_data` is the single binding for files, in both fan-out modes: one entry when fanning out, every attachment when not. `$file_data[0].filename` therefore works either way. Each entry starts with the event's `event_attachments` object. The worker resolves the file by `entity_id` and `version_index`, adds `base64`, and replaces the filename, MIME type, and size with the values of the file version it actually loaded. An event `s3ref` is passed through as metadata; it is not used as the download source.
+
+| Field | Contains |
+| --- | --- |
+| `entity_id` | File entity ID |
+| `filename` | File name returned by File API |
+| `mime_type` | MIME type returned by File API |
+| `size_bytes` | Loaded size in bytes |
+| `base64` | File content, base64-encoded |
+| `s3ref` | `bucket` and `key` of the stored file |
+| `version_index` | Which file version the event referenced |
+| `readable_size` | Human-readable size, such as `1.2 MB` |
+| `_tags` | File entity tags |
+| `relation_tags` | Tags on the relation that attached the file |
+| `category` | File category |
+| `file_date` | Document date |
+| `_created_at` | When the file entity was created |
+
+:::warning JSONata bindings
+Write `$file_data[0].filename`, including the `$`. `file_data[0].filename` reads a field named `file_data` on the event, which does not exist, and silently produces no value. Every `$` binding is checked when the recipe is saved, so a mistyped binding is rejected rather than quietly producing a missing key.
+:::
+
+Only `undefined` omits a key. `null`, `""`, `false` and `0` are values and are all sent. Write the two-arm ternary with no else branch to omit a key when a test is false:
+
+```
+{ "pin": $exists(contact.customer_pin) ? $string(contact.customer_pin) }
+```
+
+Avoid `x ? $string(x)` as the test: JSONata reads `0` and `""` as false, so a meter number of `"0"` would silently vanish. And `: undefined` is not a literal — JSONata has no `undefined` keyword, so it is a path lookup that happens to find nothing.
+
+### Sending the files unchanged
+
+Leave `body_jsonata` out and the delivery sends its files exactly as they are: the single attachment object when fanning out, the whole array when not. No mapping is needed for the common case.
+
+```json
+"steps": [
+ {
+ "method": "POST",
+ "url": "{{env.DOCUMENT_API_URL}}/documents",
+ "headers": { "Content-Type": "application/json" },
+ "response_type": "json"
+ }
+]
+```
+
+### Translating codes
+
+Codes are translated with an inline map in the expression itself. A miss yields `undefined`, so the key is simply left out; bind the map with `:=` when you also want a fallback:
+
+```
+(
+ $types := { "termination": "CANCELLATION", "complaint": "COMPLAINT" };
+ {
+ "documentType": [$lookup($types, ticket._purpose[0]), "OTHER"][0],
+ "filename": $file_data[0].filename,
+ "fileData": $file_data[0].base64
+ }
+)
+```
+
+Names the expression binds itself with `:=` are accepted alongside the built-in bindings. If the delivery must not go out at all when a code is unmapped, test for it in the step's `enabled` expression instead.
+
+## Skipping a step
+
+A step's optional `enabled` expression is JSONata returning a boolean that decides whether the step runs. Absent means it runs.
+
+```json
+{
+ "enabled": "$file_data[0].mime_type = \"application/pdf\"",
+ "method": "POST",
+ "url": "{{env.DOCUMENT_API_URL}}/documents",
+ "body_jsonata": "{ \"fileData\": $file_data[0].base64 }",
+ "response_type": "json"
+}
+```
+
+A false result is a **break**: this step is skipped and so is every step after it, and the delivery is recorded as `skipped` rather than delivered or failed. Earlier steps are not undone. The delivery is acknowledged and never retried, and a `STEP_DISABLED` monitoring event is emitted at info level — a disabled step is the configuration working, not a fault.
+
+`enabled` reads the same bindings a body does, `$steps` included, so it can branch on what an earlier step returned. This is also how a single file is filtered out: with one delivery per attachment, a false result on the first step drops that file and leaves the others untouched.
+
+```
+$count($file_data[0].relation_tags[$ = "customer-document"]) > 0
+```
+
+Return exactly `true` or `false`. An expression that throws or returns any other value fails the delivery terminally with `MAPPING_EXPRESSION_FAILED` naming the step — a missing path must not be mistaken for a deliberate skip.
+
+## URLs, headers, and environment values
+
+Two expression languages, split by what they produce. **JSONata produces data**: `body_jsonata` and `enabled`. **Handlebars composes strings**: a step's `url` and `headers`. An upload step rejects a Handlebars `body` outright.
+
+Handlebars templates render exactly once, against a single context:
+
+| Namespace | Contents |
+| --- | --- |
+| `env` | Organization environment variables and secrets |
+| `file_data` | The same array `$file_data` holds, so `{{file_data.0.filename}}` reads the first file |
+| `steps` | Previous step results, such as `{{steps.0.body.documentId}}` |
+| `auth_token` | The acquired OAuth2 token, added automatically as a `Bearer` header unless a step sets its own `Authorization` |
+
+Environment values are part of that one context, so write them plainly:
+
+```json
+"url": "{{env.DOCUMENT_API_URL}}/documents"
+```
+
+:::warning No backslash escape
+Upload templates render in a single pass. Do not write the legacy `\{{env.NAME}}` escape used by the [download direction](./file-proxy.md#environment-variable-resolution) — it is not rewritten here and renders as the literal text `{{env.NAME}}`, which the delivery then rejects rather than shipping.
+:::
+
+Two guards run on every rendered upload template, because single-pass rendering fails quietly by default. Both are terminal, and each names what to fix:
+
+- **A residual `{{` after rendering** — a configuration still carrying the `\{{` escape. Rewrite it without the backslash.
+- **A referenced `env` key absent from the environment** — checked before the URL is parsed, because an empty value in host position turns `https://{{env.host}}/document/import` into `https:///document/import`, whose host then parses as `document`. Provision the variable. A key that exists and is legitimately empty is fine; only absence fails.
+
+Store credentials and base URLs as organization environment variables using epilot's Environments & Secrets feature, following the [naming recommendations](./file-proxy.md#recommended-environment-variable-naming).
+
+## Delivery behavior
+
+The worker distinguishes three terminal outcomes: **delivered**, **skipped**, or **failed**. A skipped delivery is completed for idempotency purposes, acknowledged, and never retried; its monitoring code keeps it distinct from a workflow that reached its final step. When the final step runs, any response below `400` completes the workflow — there is no separate predicate re-judging a response the transport already accepted.
+
+- Delivery is **at least once**. Normal queue redelivery is deduplicated by a durable per-delivery record retained for 30 days.
+- A failure after the target accepts a file but before epilot commits success can still repeat the request. If the target supports an idempotency key, send it a value that is stable across retries of the same delivery. `$file_data[0].entity_id & ":" & _event_id` in `body_jsonata` distinguishes separate events for the same file; the Handlebars header context does not expose the root event ID.
+- The upload recipe is read again for queued retries, subject to a short cache. Correcting configuration affects later attempts without replaying the source event.
+- `upload.max_delivery_attempts` defaults to 8 and supports 1–100 attempts with jittered backoff. The default schedules at most 7 delays, totaling roughly 7 hours 40 minutes before jitter, so a normal ERP maintenance window does not immediately exhaust them.
+- `upload.max_file_bytes` is the per-file ceiling. It defaults to the platform maximum of 100 MiB (`104857600`) and cannot exceed it. Known size is checked before fetch; the limit is always enforced while buffering.
+- `upload.max_total_bytes` is the ceiling for all of a delivery's files together, in bytes, and cannot exceed the same platform maximum. It only has an effect with fan-out disabled, where one delivery carries every attachment and base64 inflates each by about a third.
+- External HTTP responses below `400` are accepted. `408`, `429`, and `5xx` responses are retried; other `4xx` responses are terminal. Timeouts, OAuth refresh failures, and transient file-fetch failures are retried up to the configured attempt limit.
+- Configuration and data errors that will not improve on retry — such as a missing file, an expression that cannot evaluate, a body expression that returns something other than an object or array, or an unresolvable step template — fail terminally. JSONata expressions, Handlebars syntax, and `$` bindings are also checked when the recipe is saved.
+- Terminal and exhausted deliveries are recorded as failed and completed without deliberately filling the dead-letter queue with permanent errors.
+
+## Monitoring
+
+File-delivery monitoring uses the source Event Catalog `_event_id` as both `event_id` and `correlation_id`, so all attachments from one event stay in one trace. Base details identify the event, mapping, upload recipe, attachment, item index, and item count.
+
+Worker terminal outcomes add the attempt number. When the file proxy calls the target directly, a terminal record also includes the final request and response when available; credentials are redacted and long values such as base64 file content are elided. Retry records do not duplicate that exchange. When a recipe uses `secure_proxy`, the secure proxy records the partner exchange under its own use case instead.
+
+Worker terminal outcomes are emitted twice: once for the outbound use case and once for the upload `file_proxy` use case. Enqueue, empty-fan-out, and retry records are attributed only to the outbound use case.
+
+| Level | Code | Meaning |
+| --- | --- | --- |
+| Success | `FILE_PROXY_UPLOADED` | The workflow reached its final step with a response below `400`. |
+| Info | `FILE_PROXY_UPLOAD_ENQUEUED` | The mapping queued its deliveries; `item_count` records how many. |
+| Info | `FAN_OUT_EMPTY` | The event carried no attachments to send. |
+| Info | `STEP_DISABLED` | A step's `enabled` expression returned false, so the remaining workflow was skipped. Includes the zero-based `step_index`. A legacy or malformed queued item with no usable attachment also completes through this code, without `step_index`. |
+| Warning | `FILE_PROXY_UPLOAD_RETRYING` | The delivery failed and will be attempted again. |
+| Error | `FILE_PROXY_UPLOAD_FAILED` | Delivery failed terminally or exhausted attempts. |
+| Error | `FILE_FETCH_FAILED` | File API did not return usable content. |
+| Error | `FILE_TOO_LARGE` | A file, or all files in one non-fan-out delivery together, exceeded the recipe or platform limit. |
+| Error | `ATTACHMENT_NOT_FOUND` | The referenced file entity or version was not found. |
+| Error | `MAPPING_EXPRESSION_FAILED` | A `body_jsonata` or `enabled` expression could not be evaluated, or produced an unusable result. |
+
+General configuration codes, including `USE_CASE_NOT_FOUND`, `USE_CASE_DISABLED`, `USE_CASE_INVALID_TYPE`, and `USE_CASE_MISSING_CONFIG`, can also apply. These are epilot-produced monitoring codes; they are separate from the `EXTERNAL_*` codes described in [External Monitoring Events](./external-monitoring-events.md).
+
+`REQUIRED_PARAM_MISSING`, `LOOKUP_UNMAPPED`, and `FAN_OUT_INVALID_RESULT` remain in the shared code enum but are no longer produced by this upload path; their configuration mechanisms were removed.
+
+## Troubleshooting
+
+| Symptom | Check |
+| --- | --- |
+| The use case cannot be saved | A `file_proxy` mapping requires an event whose catalog schema declares `event_attachments`. The `400` message names the event. |
+| Target is unresolved | The delivery's `use_case_slug` must match an enabled upload-direction `file_proxy` use case in the same integration. Check `USE_CASE_NOT_FOUND`, `USE_CASE_DISABLED`, or `USE_CASE_INVALID_TYPE` monitoring. |
+| Mapped filename or bytes are empty | File bindings require `$file_data`, including the `$`, and an index: `$file_data[0].base64`. |
+| Target URL or credentials are empty | Write `{{env.NAME}}` without a leading backslash, and confirm the variable exists in the organization's environment. |
+| An optional field is missing from the body | Only `undefined` omits a key. Check the test in the two-arm ternary — `0` and `""` are false in JSONata. |
+| Everything reports skipped | An `enabled` expression is returning false. `STEP_DISABLED` names the `step_index`; if it is absent, inspect the queued attachment's `entity_id`. |
+| Event succeeds without uploading | Inspect `FAN_OUT_EMPTY` and `event_filter`; the event may carry no attachments. |
+| `CustomerRequestSubmitted` has no files | Place its Automation trigger action after the workflow step that writes the ticket's file relations. An empty attachment array is valid and is not replayed after a later relation update. |
+| File fetch fails | Verify `entity_id`, `version_index`, and the configured file-size limits. |
+| Target `4xx` is not retried | This is expected except for `408` and `429`; correct the request mapping or target configuration. |
+
+## Related documentation
+
+- [Core Events](/docs/integrations/core-events)
+- [Configuration](./configuration.md)
+- [File Proxy downloads](./file-proxy.md)
+- [External Monitoring Events](./external-monitoring-events.md)
+- [Pollable Outbound](./pollable-outbound.md)
diff --git a/docs/integrations/integration-toolkit/overview.md b/docs/integrations/integration-toolkit/overview.md
index 3c02db60..39ea430a 100644
--- a/docs/integrations/integration-toolkit/overview.md
+++ b/docs/integrations/integration-toolkit/overview.md
@@ -35,6 +35,7 @@ The Integration Toolkit is composed of the following components. Each plays a sp
| **[Pollable Outbound](./pollable-outbound.md)** | Pull-based outbound delivery — ERPs poll a queue instead of receiving webhooks | In progress |
| **[JSONata Mapping](#jsonata-mapping)** | Transformation language for inbound and outbound data | Stable |
| **[File Proxy](./file-proxy.md)** | Serve files from external archives on demand without migrating them into epilot | Stable |
+| **[Outbound File Delivery](./outbound-file-delivery.md)** | Deliver files referenced by epilot events to external document APIs | Stable |
| **[Managed Calls](#managed-calls)** | Synchronous external API calls with JSONata mapping via connector integrations | Stable |
| **[Secure Proxy](#secure-proxy)** | Route HTTP requests through epilot's secure proxy for static IP egress or VPN access | Stable |
| **[Monitoring and ACKs](#monitoring-and-acks)** | Central logging, error tracking, and event replay | In progress |
@@ -87,6 +88,8 @@ See the [Use Cases](./use-cases.md) page for a complete list of inbound and outb
The [File Proxy](./file-proxy.md) enables epilot to serve files from external document systems (e.g., ERP archives, DMS) on demand. Instead of migrating file content during inbound sync, file entities are created with a `custom_download_url` pointing to the proxy. When a user views the file, the proxy fetches the document from the external system in real time using a declarative, multi-step HTTP configuration.
+The upload direction is documented separately as [Outbound File Delivery](./outbound-file-delivery.md). It subscribes to Event Catalog events, fans one event out into per-file deliveries, fetches the referenced epilot files, and runs a declarative external HTTP workflow with independent retries and monitoring.
+
### Managed Calls
Managed Calls enable synchronous API calls to external partner systems with built-in authentication, JSONata mapping, and optional inbound routing. They are configured as `managed_call` use cases within `connector`-type integrations.
@@ -152,7 +155,7 @@ Push epilot events to your ERP via webhooks. Typical flows:
- Self-service requests (IBAN changes, installment adjustments)
- Meter reading submissions
-Outbound events use [Core Events](/docs/integrations/core-events) and are delivered through [Webhooks](/docs/integrations/webhooks). JSONata transforms simplify payloads before delivery.
+Outbound events use [Core Events](/docs/integrations/core-events). They can be delivered through [Webhooks](/docs/integrations/webhooks), made available through [Pollable Outbound](./pollable-outbound.md), or used to [deliver referenced files](./outbound-file-delivery.md) to an external API. JSONata transforms simplify event and file payloads before delivery.
For ERPs that cannot receive webhooks (firewalled, on-prem, or batch-oriented systems), [Pollable Outbound](./pollable-outbound.md) offers a pull-based alternative: your middleware polls a queue inside the ERP Integration API on its own schedule and acknowledges items once consumed.
diff --git a/static/events/CustomerRequestSubmitted.config.json b/static/events/CustomerRequestSubmitted.config.json
new file mode 100644
index 00000000..47b39976
--- /dev/null
+++ b/static/events/CustomerRequestSubmitted.config.json
@@ -0,0 +1,169 @@
+{
+ "event_name": "CustomerRequestSubmitted",
+ "event_title": "Customer Request Submitted",
+ "event_description": "Explicitly triggered by Automation after a customer request ticket and its file attachments are ready",
+ "event_version": "1.0",
+ "event_tags": [
+ "builtin",
+ "erp",
+ "automation",
+ "ticket",
+ "attachment",
+ "customer-request"
+ ],
+ "schema_fields": {
+ "event_attachments": {
+ "json_schema": {
+ "type": "array",
+ "items": {
+ "type": "object",
+ "properties": {
+ "entity_id": {
+ "type": "string",
+ "format": "uuid",
+ "description": "Entity ID of the file"
+ },
+ "filename": {
+ "type": "string",
+ "description": "Name of the file"
+ },
+ "mime_type": {
+ "type": "string",
+ "description": "MIME type of the file (e.g., application/pdf)"
+ },
+ "size_bytes": {
+ "type": "integer",
+ "minimum": 0,
+ "description": "File size in bytes"
+ },
+ "s3ref": {
+ "type": "object",
+ "properties": {
+ "bucket": {
+ "type": "string",
+ "description": "S3 bucket name"
+ },
+ "key": {
+ "type": "string",
+ "description": "S3 object key"
+ }
+ },
+ "required": [
+ "bucket",
+ "key"
+ ],
+ "additionalProperties": false,
+ "description": "S3 reference for the file content"
+ },
+ "version_index": {
+ "type": "integer",
+ "minimum": 0,
+ "description": "Latest version index of the file (versions[].length - 1, or 0 if no versions array)"
+ },
+ "readable_size": {
+ "type": "string",
+ "description": "Human-readable file size (e.g., \"200 KB\")"
+ },
+ "_tags": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ },
+ "description": "Tags on the file entity (semantic labels set by uploader/automation)"
+ },
+ "relation_tags": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ },
+ "description": "Tags on the ticket→file relation (e.g. journey step label like \"_hidden_ Neuer Schritt - Nachweis\" plus the upload block's configured tags)"
+ },
+ "category": {
+ "type": "string",
+ "description": "File category select value (e.g., \"address-suggestions\", \"unknown\")"
+ },
+ "file_date": {
+ "type": "string",
+ "description": "User-provided file date"
+ },
+ "_created_at": {
+ "type": "string",
+ "format": "date-time",
+ "description": "When the file entity was created — useful for time-window filtering"
+ }
+ },
+ "required": [
+ "entity_id",
+ "version_index"
+ ],
+ "additionalProperties": false
+ },
+ "description": "All files related to the ticket, empty when the request carries none. A single submission can carry many documents across several upload blocks. Consumers identify a given upload via `relation_tags` / `_tags` and scope the use case via the `_purpose` taxonomy IDs on the ticket node (IDs only — resolving them to names is up to the consumer)."
+ },
+ "required": true,
+ "graph_source": "[ticket_files.{\"entity_id\": _id, \"filename\": filename, \"mime_type\": mime_type, \"size_bytes\": size_bytes, \"s3ref\": s3ref, \"version_index\": ($count(versions) > 0 ? $count(versions) - 1 : 0), \"readable_size\": readable_size, \"_tags\": _tags, \"relation_tags\": relation_tags, \"category\": category, \"file_date\": file_date, \"_created_at\": _created_at}]"
+ }
+ },
+ "entity_graph": {
+ "nodes": [
+ {
+ "id": "ticket",
+ "schema": "ticket",
+ "cardinality": "one"
+ },
+ {
+ "id": "submission",
+ "schema": "submission",
+ "cardinality": "one",
+ "fields": [
+ "_id"
+ ]
+ },
+ {
+ "id": "contact",
+ "schema": "contact",
+ "cardinality": "one"
+ },
+ {
+ "id": "contract",
+ "schema": "contract",
+ "cardinality": "one"
+ },
+ {
+ "id": "billing_account",
+ "schema": "billing_account",
+ "cardinality": "one"
+ },
+ {
+ "id": "ticket_files",
+ "schema": "file",
+ "cardinality": "many"
+ }
+ ],
+ "edges": [
+ {
+ "from": "ticket",
+ "to": "submission"
+ },
+ {
+ "from": "ticket",
+ "to": "contact"
+ },
+ {
+ "from": "ticket",
+ "to": "contract"
+ },
+ {
+ "from": "ticket",
+ "to": "billing_account"
+ },
+ {
+ "from": "ticket",
+ "to": "ticket_files"
+ }
+ ]
+ },
+ "automation_trigger": true,
+ "automation_trigger_only": true,
+ "automation_trigger_seed_node": "ticket"
+}
\ No newline at end of file
diff --git a/static/events/CustomerRequestSubmitted.sample.json b/static/events/CustomerRequestSubmitted.sample.json
new file mode 100644
index 00000000..adbfefaf
--- /dev/null
+++ b/static/events/CustomerRequestSubmitted.sample.json
@@ -0,0 +1,296 @@
+{
+ "_org_id": "123",
+ "_event_time": "1970-01-01T00:00:00.000Z",
+ "_event_id": "string",
+ "_event_name": "CustomerRequestSubmitted",
+ "_event_version": "1.0",
+ "_event_source": "automation",
+ "_ack_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
+ "_trigger_source_type": "automation",
+ "_trigger_source": "execution-id/action-id",
+ "_has_pending_changesets": true,
+ "_changeset_edit_modes": [
+ "string"
+ ],
+ "_changeset_attributes": [
+ "string"
+ ],
+ "_changeset_edit_modes_by_attribute": {},
+ "event_attachments": [
+ {
+ "entity_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
+ "filename": "string",
+ "mime_type": "string",
+ "size_bytes": 0,
+ "s3ref": {
+ "bucket": "string",
+ "key": "string"
+ },
+ "version_index": 0,
+ "readable_size": "string",
+ "_tags": [
+ "string"
+ ],
+ "relation_tags": [
+ "string"
+ ],
+ "category": "string",
+ "file_date": "string",
+ "_created_at": "1970-01-01T00:00:00.000Z"
+ }
+ ],
+ "ticket": {
+ "_schema": "ticket",
+ "_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
+ "_org": "123",
+ "_title": "John Doe",
+ "_tags": [
+ "family:label"
+ ],
+ "_purpose": {},
+ "ticket_title": "string",
+ "customer": {},
+ "ticket_number": "string",
+ "status": "Open",
+ "payment": [
+ {
+ "type": "payment_sepa",
+ "data": {
+ "bank_name": "Deutsche Bank",
+ "iban": "DE89370400440532013000",
+ "bic_number": "DEUTDEFF",
+ "fullname": "John Doe"
+ }
+ }
+ ],
+ "source": {},
+ "workflows": {},
+ "_files": {},
+ "_created_at": "1970-01-01T00:00:00.000Z",
+ "_updated_at": "1970-01-01T00:00:00.000Z"
+ },
+ "submission": {
+ "_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
+ "_schema": "submission",
+ "_org": "123"
+ },
+ "contact": {
+ "_schema": "contact",
+ "_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
+ "_org": "123",
+ "_title": "John Doe",
+ "_tags": [
+ "family:label"
+ ],
+ "_purpose": {},
+ "salutation": "null",
+ "title": "null",
+ "first_name": "string",
+ "last_name": "string",
+ "customer_number": "string",
+ "birthdate": "2025-01-01",
+ "email": [
+ {
+ "_tags": [
+ "string"
+ ],
+ "email": "user@example.com"
+ }
+ ],
+ "phone": [
+ {
+ "_tags": [
+ "string"
+ ],
+ "phone": "+491234567890"
+ }
+ ],
+ "communication_preference": "postal",
+ "address": [
+ {
+ "_tags": [
+ "billing"
+ ],
+ "street": "Hauptstraße",
+ "street_number": "123",
+ "postal_code": "50668",
+ "city": "Cologne",
+ "country": "DE",
+ "additional_info": "string",
+ "company_name": "string",
+ "first_name": "string",
+ "last_name": "string",
+ "salutation": "string",
+ "title": "string",
+ "coordinates": "50.948484, 6.943147",
+ "plot_of_land": "string",
+ "plot_area": "string",
+ "start_date": "1970-01-01",
+ "end_date": "1970-01-01"
+ }
+ ],
+ "payment": [
+ {
+ "type": "payment_sepa",
+ "data": {
+ "bank_name": "Deutsche Bank",
+ "iban": "DE89370400440532013000",
+ "bic_number": "DEUTDEFF",
+ "fullname": "John Doe"
+ }
+ }
+ ],
+ "account": {},
+ "marketing_permission": true,
+ "contact_owner": {},
+ "consent_email_marketing": {},
+ "consent_sms_marketing": {},
+ "consent_phone_call": {},
+ "consent_print_marketing": {},
+ "portal_users": {},
+ "opportunities": {},
+ "orders": {},
+ "contracts": {},
+ "external_id": "string",
+ "workflows": {},
+ "_files": {},
+ "_created_at": "1970-01-01T00:00:00.000Z",
+ "_updated_at": "1970-01-01T00:00:00.000Z"
+ },
+ "contract": {
+ "_schema": "contract",
+ "_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
+ "_org": "123",
+ "_title": "John Doe",
+ "_tags": [
+ "family:label"
+ ],
+ "_purpose": {},
+ "contract_name": "string",
+ "contract_number": "string",
+ "assignee": {},
+ "payment": {},
+ "status": "draft",
+ "description": "string",
+ "billing_account": {},
+ "account_number": "string",
+ "branch": "power",
+ "move_in_date": "2025-01-01",
+ "move_out_date": "2025-01-01",
+ "billing_address": {},
+ "delivery_address": [
+ {
+ "_tags": [
+ "billing"
+ ],
+ "street": "Hauptstraße",
+ "street_number": "123",
+ "postal_code": "50668",
+ "city": "Cologne",
+ "country": "DE",
+ "additional_info": "string",
+ "company_name": "string",
+ "first_name": "string",
+ "last_name": "string",
+ "salutation": "string",
+ "title": "string",
+ "coordinates": "50.948484, 6.943147",
+ "plot_of_land": "string",
+ "plot_area": "string",
+ "start_date": "1970-01-01",
+ "end_date": "1970-01-01"
+ }
+ ],
+ "additional_addresses": {},
+ "termination_date": "2025-01-01",
+ "termination_reason": "string",
+ "start_date": "2025-01-01",
+ "end_date": "2025-01-01",
+ "customer": {},
+ "order": {},
+ "type": "one_time",
+ "billing_period": "weekly",
+ "billing_duration_amount": "string",
+ "billing_duration_unit": "weeks",
+ "notice_time_amount": "string",
+ "notice_time_unit": "weeks",
+ "termination_time_amount": "string",
+ "termination_time_unit": "weeks",
+ "renewal_duration_amount": "string",
+ "renewal_duration_unit": "weeks",
+ "billing_due_day": "string",
+ "installment_amount_currency": "EUR",
+ "installment_amount_decimal": "123.45",
+ "installment_amount": 0,
+ "balance_currency": "EUR",
+ "balance_decimal": "123.45",
+ "balance": 0,
+ "meters": {},
+ "last_sync_at": "2025-01-01",
+ "external_id": "string",
+ "workflows": {},
+ "line_items": {},
+ "total_details": {},
+ "_files": {},
+ "_created_at": "1970-01-01T00:00:00.000Z",
+ "_updated_at": "1970-01-01T00:00:00.000Z"
+ },
+ "billing_account": {
+ "_schema": "billing_account",
+ "_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
+ "_org": "123",
+ "_title": "John Doe",
+ "_tags": [
+ "family:label"
+ ],
+ "billing_account_number": "string",
+ "balance_currency": "EUR",
+ "balance_decimal": "123.45",
+ "balance": 0,
+ "billing_contact": {},
+ "billing_address": {},
+ "payment_method": [
+ {
+ "type": "payment_sepa",
+ "data": {
+ "bank_name": "Deutsche Bank",
+ "iban": "DE89370400440532013000",
+ "bic_number": "DEUTDEFF",
+ "fullname": "John Doe"
+ }
+ }
+ ],
+ "contracts": {},
+ "external_id": "string",
+ "workflows": {},
+ "_files": {},
+ "_created_at": "1970-01-01T00:00:00.000Z",
+ "_updated_at": "1970-01-01T00:00:00.000Z"
+ },
+ "ticket_files": [
+ {
+ "_schema": "file",
+ "_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
+ "_org": "123",
+ "_title": "John Doe",
+ "_tags": [
+ "family:label"
+ ],
+ "_purpose": {},
+ "_thumbnail": {},
+ "filename": "string",
+ "mime_type": "string",
+ "type": "document",
+ "readable_size": "string",
+ "language": "de",
+ "alt_text": "string",
+ "access_control": "private",
+ "public_url": "string",
+ "shared_with_end_customer": true,
+ "category": "address-suggestions",
+ "_files": {},
+ "_created_at": "1970-01-01T00:00:00.000Z",
+ "_updated_at": "1970-01-01T00:00:00.000Z"
+ }
+ ]
+}
\ No newline at end of file
diff --git a/static/events/CustomerRequestSubmitted.schema.json b/static/events/CustomerRequestSubmitted.schema.json
new file mode 100644
index 00000000..f17ddca8
--- /dev/null
+++ b/static/events/CustomerRequestSubmitted.schema.json
@@ -0,0 +1,2386 @@
+{
+ "type": "object",
+ "properties": {
+ "_org_id": {
+ "type": "string",
+ "description": "epilot tenant/organization ID",
+ "example": "123"
+ },
+ "_event_time": {
+ "type": "string",
+ "format": "date-time",
+ "description": "ISO 8601 timestamp when event occurred"
+ },
+ "_event_id": {
+ "type": "string",
+ "description": "Unique event identifier (ULID)"
+ },
+ "_event_name": {
+ "type": "string",
+ "description": "Event name from catalog",
+ "example": "CustomerRequestSubmitted"
+ },
+ "_event_version": {
+ "type": "string",
+ "description": "Schema version number",
+ "example": "1.0"
+ },
+ "_event_source": {
+ "type": "string",
+ "description": "Source that triggered the event",
+ "enum": [
+ "automation"
+ ],
+ "example": "automation"
+ },
+ "_ack_id": {
+ "type": "string",
+ "format": "uuid",
+ "description": "Acknowledgement ID for tracking event delivery (UUID)"
+ },
+ "_trigger_source_type": {
+ "type": "string",
+ "description": "The type of system that triggered the event. Examples: api, automation, operation, portal_user",
+ "enum": [
+ "automation"
+ ],
+ "example": "automation"
+ },
+ "_trigger_source": {
+ "type": "string",
+ "description": "Identifier of the specific trigger source. Examples: user ID, automation execution ID, activity ID, portal user email",
+ "minLength": 1,
+ "example": "execution-id/action-id"
+ },
+ "_has_pending_changesets": {
+ "type": "boolean",
+ "description": "True when the triggering entity (or meter counter) has at least one pending changeset"
+ },
+ "_changeset_edit_modes": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ },
+ "description": "Distinct edit modes across the PENDING changesets — only \"external\" (synced from an external system) or \"approval\" (awaiting approval). A \"direct\" write is applied immediately, is not a pending changeset, and so never appears here."
+ },
+ "_changeset_attributes": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ },
+ "description": "Keys identifying pending changesets. For attribute changesets these are attribute names (e.g. `billing_cycle`); for meter-reading changesets these are the changeset IDs from `meter._meter_readings_changeset`"
+ },
+ "_changeset_edit_modes_by_attribute": {
+ "type": "object",
+ "additionalProperties": {
+ "anyOf": [
+ {
+ "not": {}
+ },
+ {
+ "type": "string"
+ }
+ ]
+ },
+ "description": "Per-key edit mode map; keys are a subset of _changeset_attributes (attribute names for entity changesets, changeset IDs for meter-reading changesets) — only those whose pending changeset has an \"external\" or \"approval\" mode. A \"direct\"/unrecognised mode is omitted here, though the key still appears in _changeset_attributes. Enables fine-grained webhook conditions such as `_changeset_edit_modes_by_attribute.billing_cycle equals approval`"
+ },
+ "event_attachments": {
+ "type": "array",
+ "items": {
+ "type": "object",
+ "properties": {
+ "entity_id": {
+ "type": "string",
+ "format": "uuid",
+ "description": "Entity ID of the file"
+ },
+ "filename": {
+ "type": "string",
+ "description": "Name of the file"
+ },
+ "mime_type": {
+ "type": "string",
+ "description": "MIME type of the file (e.g., application/pdf)"
+ },
+ "size_bytes": {
+ "type": "integer",
+ "minimum": 0,
+ "description": "File size in bytes"
+ },
+ "s3ref": {
+ "type": "object",
+ "properties": {
+ "bucket": {
+ "type": "string",
+ "description": "S3 bucket name"
+ },
+ "key": {
+ "type": "string",
+ "description": "S3 object key"
+ }
+ },
+ "required": [
+ "bucket",
+ "key"
+ ],
+ "additionalProperties": false,
+ "description": "S3 reference for the file content"
+ },
+ "version_index": {
+ "type": "integer",
+ "minimum": 0,
+ "description": "Latest version index of the file (versions[].length - 1, or 0 if no versions array)"
+ },
+ "readable_size": {
+ "type": "string",
+ "description": "Human-readable file size (e.g., \"200 KB\")"
+ },
+ "_tags": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ },
+ "description": "Tags on the file entity (semantic labels set by uploader/automation)"
+ },
+ "relation_tags": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ },
+ "description": "Tags on the ticket→file relation (e.g. journey step label like \"_hidden_ Neuer Schritt - Nachweis\" plus the upload block's configured tags)"
+ },
+ "category": {
+ "type": "string",
+ "description": "File category select value (e.g., \"address-suggestions\", \"unknown\")"
+ },
+ "file_date": {
+ "type": "string",
+ "description": "User-provided file date"
+ },
+ "_created_at": {
+ "type": "string",
+ "format": "date-time",
+ "description": "When the file entity was created — useful for time-window filtering"
+ }
+ },
+ "required": [
+ "entity_id",
+ "version_index"
+ ],
+ "additionalProperties": false
+ },
+ "description": "All files related to the ticket, empty when the request carries none. A single submission can carry many documents across several upload blocks. Consumers identify a given upload via `relation_tags` / `_tags` and scope the use case via the `_purpose` taxonomy IDs on the ticket node (IDs only — resolving them to names is up to the consumer)."
+ },
+ "ticket": {
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
+ "type": "object",
+ "required": [
+ "_schema"
+ ],
+ "properties": {
+ "_schema": {
+ "readOnly": true,
+ "type": "string",
+ "pattern": "^[a-zA-Z0-9_-]+$",
+ "example": "ticket",
+ "description": "URL-friendly identifier for the entity schema"
+ },
+ "_id": {
+ "readOnly": true,
+ "type": "string",
+ "format": "uuid",
+ "example": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
+ "description": "Unique identifier for the entity"
+ },
+ "_org": {
+ "type": "string",
+ "description": "Organization Id the entity belongs to",
+ "readOnly": true,
+ "example": "123"
+ },
+ "_title": {
+ "readOnly": true,
+ "type": "string",
+ "nullable": true,
+ "example": "John Doe",
+ "description": "Generated title for the entity based on schema title_template"
+ },
+ "_tags": {
+ "type": "array",
+ "nullable": true,
+ "items": {
+ "type": "string"
+ },
+ "example": [
+ "family:label"
+ ],
+ "description": "Entity Labels"
+ },
+ "_purpose": {
+ "$ref": "#/definitions/PurposeAttribute",
+ "description": "Purposes"
+ },
+ "ticket_title": {
+ "type": "string",
+ "description": "Ticket Title"
+ },
+ "customer": {
+ "$ref": "#/definitions/BaseRelation",
+ "description": "Customer"
+ },
+ "ticket_number": {
+ "type": "string",
+ "description": "Ticket Number"
+ },
+ "status": {
+ "type": "string",
+ "enum": [
+ "Open",
+ "In Progress",
+ "Escalated",
+ "Closed",
+ "Canceled"
+ ],
+ "description": "Status"
+ },
+ "payment": {
+ "type": "array",
+ "items": {
+ "type": "object",
+ "description": "Payment method details",
+ "properties": {
+ "type": {
+ "type": "string",
+ "enum": [
+ "payment_sepa",
+ "payment_invoice",
+ "payment_cash"
+ ],
+ "example": "payment_sepa"
+ },
+ "data": {
+ "type": "object",
+ "properties": {
+ "bank_name": {
+ "type": "string",
+ "example": "Deutsche Bank"
+ },
+ "iban": {
+ "type": "string",
+ "example": "DE89370400440532013000"
+ },
+ "bic_number": {
+ "type": "string",
+ "example": "DEUTDEFF"
+ },
+ "fullname": {
+ "type": "string",
+ "example": "John Doe"
+ }
+ }
+ }
+ }
+ },
+ "description": "Payment"
+ },
+ "source": {
+ "$ref": "#/definitions/LinkAttribute",
+ "description": "Source"
+ },
+ "workflows": {
+ "$ref": "#/definitions/WorkflowOverview"
+ },
+ "_files": {
+ "$ref": "#/definitions/BaseRelation"
+ },
+ "_created_at": {
+ "readOnly": true,
+ "type": "string",
+ "format": "date-time",
+ "nullable": true,
+ "description": "Timestamp of entity creation"
+ },
+ "_updated_at": {
+ "readOnly": true,
+ "type": "string",
+ "format": "date-time",
+ "nullable": true,
+ "description": "Timestamp of entity last update"
+ }
+ },
+ "description": "ticket entity (hydrated from entity graph)"
+ },
+ "submission": {
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
+ "type": "object",
+ "required": [
+ "_schema"
+ ],
+ "properties": {
+ "_id": {
+ "readOnly": true,
+ "type": "string",
+ "format": "uuid",
+ "example": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
+ "description": "Unique identifier for the entity"
+ },
+ "_schema": {
+ "readOnly": true,
+ "type": "string",
+ "pattern": "^[a-zA-Z0-9_-]+$",
+ "example": "submission",
+ "description": "URL-friendly identifier for the entity schema"
+ },
+ "_org": {
+ "type": "string",
+ "description": "Organization Id the entity belongs to",
+ "readOnly": true,
+ "example": "123"
+ }
+ },
+ "description": "submission entity (hydrated from entity graph)"
+ },
+ "contact": {
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
+ "type": "object",
+ "required": [
+ "_schema"
+ ],
+ "properties": {
+ "_schema": {
+ "readOnly": true,
+ "type": "string",
+ "pattern": "^[a-zA-Z0-9_-]+$",
+ "example": "contact",
+ "description": "URL-friendly identifier for the entity schema"
+ },
+ "_id": {
+ "readOnly": true,
+ "type": "string",
+ "format": "uuid",
+ "example": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
+ "description": "Unique identifier for the entity"
+ },
+ "_org": {
+ "type": "string",
+ "description": "Organization Id the entity belongs to",
+ "readOnly": true,
+ "example": "123"
+ },
+ "_title": {
+ "readOnly": true,
+ "type": "string",
+ "nullable": true,
+ "example": "John Doe",
+ "description": "Generated title for the entity based on schema title_template"
+ },
+ "_tags": {
+ "type": "array",
+ "nullable": true,
+ "items": {
+ "type": "string"
+ },
+ "example": [
+ "family:label"
+ ],
+ "description": "Entity Labels"
+ },
+ "_purpose": {
+ "$ref": "#/definitions/PurposeAttribute",
+ "description": "Purposes"
+ },
+ "salutation": {
+ "type": "string",
+ "enum": [
+ "null",
+ "Mr.",
+ "Ms. / Mrs.",
+ "Company",
+ "Contact Person",
+ "Company/Contact Person",
+ "Spouse",
+ "Family",
+ "Ownership",
+ "Assembly",
+ "Other"
+ ],
+ "description": "Salutation"
+ },
+ "title": {
+ "type": "string",
+ "enum": [
+ "null",
+ "Dr.",
+ "Prof.",
+ "Prof. Dr."
+ ],
+ "description": "Title"
+ },
+ "first_name": {
+ "type": "string",
+ "description": "First Name"
+ },
+ "last_name": {
+ "type": "string",
+ "description": "Last Name"
+ },
+ "customer_number": {
+ "type": "string",
+ "description": "Customer Number"
+ },
+ "birthdate": {
+ "type": "string",
+ "format": "date",
+ "example": "2025-01-01",
+ "description": "Date of Birth"
+ },
+ "email": {
+ "type": "array",
+ "items": {
+ "type": "object",
+ "properties": {
+ "_tags": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "email": {
+ "type": "string",
+ "format": "email"
+ }
+ },
+ "required": [
+ "email"
+ ]
+ },
+ "description": "Email"
+ },
+ "phone": {
+ "type": "array",
+ "items": {
+ "type": "object",
+ "properties": {
+ "_tags": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "phone": {
+ "type": "string",
+ "example": "+491234567890"
+ }
+ },
+ "required": [
+ "phone"
+ ]
+ },
+ "description": "Phone"
+ },
+ "communication_preference": {
+ "type": "string",
+ "enum": [
+ "postal",
+ "portal"
+ ],
+ "description": "Standard communication method"
+ },
+ "address": {
+ "type": "array",
+ "items": {
+ "type": "object",
+ "description": "Address attribute",
+ "additionalProperties": true,
+ "properties": {
+ "_tags": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ },
+ "example": [
+ "billing"
+ ]
+ },
+ "street": {
+ "type": "string",
+ "description": "The first line of the address. Typically the street address or PO Box number.",
+ "nullable": true,
+ "example": "Hauptstraße"
+ },
+ "street_number": {
+ "type": "string",
+ "description": "The second line of the address. Typically the number of the apartment, suite, or unit.",
+ "nullable": true,
+ "example": "123"
+ },
+ "postal_code": {
+ "type": "string",
+ "description": "The postal code for the address.",
+ "nullable": true,
+ "example": "50668"
+ },
+ "city": {
+ "type": "string",
+ "description": "The name of the city, district, village, or town.",
+ "nullable": true,
+ "example": "Cologne"
+ },
+ "country": {
+ "type": "string",
+ "description": "The two-letter code for the country of the address.",
+ "nullable": true,
+ "example": "DE"
+ },
+ "additional_info": {
+ "type": "string",
+ "description": "An additional description for the address",
+ "nullable": true
+ },
+ "company_name": {
+ "type": "string",
+ "description": "the company name, usually used as extra delivery instructions",
+ "nullable": true
+ },
+ "first_name": {
+ "type": "string",
+ "description": "the first name of the recipient, usually used as extra delivery instructions",
+ "nullable": true
+ },
+ "last_name": {
+ "type": "string",
+ "description": "the last name of the recipient, usually used as extra delivery instructions",
+ "nullable": true
+ },
+ "salutation": {
+ "type": "string",
+ "description": "the salutation of the recipient, usually used as extra delivery instructions",
+ "nullable": true
+ },
+ "title": {
+ "type": "string",
+ "description": "the title of the recipient, usually used as extra delivery instructions",
+ "nullable": true
+ },
+ "coordinates": {
+ "type": "string",
+ "description": "Geographical coordinates (latitude, longitude)",
+ "nullable": true,
+ "example": "50.948484, 6.943147"
+ },
+ "plot_of_land": {
+ "type": "string",
+ "description": "Plot of land information",
+ "nullable": true
+ },
+ "plot_area": {
+ "type": "string",
+ "description": "Area of the plot",
+ "nullable": true
+ },
+ "start_date": {
+ "type": "string",
+ "description": "Start date for the address",
+ "format": "date",
+ "nullable": true
+ },
+ "end_date": {
+ "type": "string",
+ "description": "End date for the address",
+ "format": "date",
+ "nullable": true
+ }
+ }
+ },
+ "description": "Address"
+ },
+ "payment": {
+ "type": "array",
+ "items": {
+ "type": "object",
+ "description": "Payment method details",
+ "properties": {
+ "type": {
+ "type": "string",
+ "enum": [
+ "payment_sepa",
+ "payment_invoice",
+ "payment_cash"
+ ],
+ "example": "payment_sepa"
+ },
+ "data": {
+ "type": "object",
+ "properties": {
+ "bank_name": {
+ "type": "string",
+ "example": "Deutsche Bank"
+ },
+ "iban": {
+ "type": "string",
+ "example": "DE89370400440532013000"
+ },
+ "bic_number": {
+ "type": "string",
+ "example": "DEUTDEFF"
+ },
+ "fullname": {
+ "type": "string",
+ "example": "John Doe"
+ }
+ }
+ }
+ }
+ },
+ "description": "Payment"
+ },
+ "account": {
+ "$ref": "#/definitions/BaseRelation",
+ "description": "Account"
+ },
+ "marketing_permission": {
+ "type": "boolean",
+ "description": "Marketing Permission"
+ },
+ "contact_owner": {
+ "$ref": "#/definitions/User",
+ "description": "Contact Owner"
+ },
+ "consent_email_marketing": {
+ "$ref": "#/definitions/ConsentAttribute",
+ "description": "Email Marketing"
+ },
+ "consent_sms_marketing": {
+ "$ref": "#/definitions/ConsentAttribute",
+ "description": "SMS Marketing"
+ },
+ "consent_phone_call": {
+ "$ref": "#/definitions/ConsentAttribute",
+ "description": "Direct Phone Marketing"
+ },
+ "consent_print_marketing": {
+ "$ref": "#/definitions/ConsentAttribute",
+ "description": "Print Marketing"
+ },
+ "portal_users": {
+ "$ref": "#/definitions/BaseRelation",
+ "description": "Portal Users"
+ },
+ "opportunities": {
+ "$ref": "#/definitions/BaseRelation",
+ "description": "Opportunities"
+ },
+ "orders": {
+ "$ref": "#/definitions/BaseRelation",
+ "description": "Orders"
+ },
+ "contracts": {
+ "$ref": "#/definitions/BaseRelation",
+ "description": "Contracts"
+ },
+ "external_id": {
+ "type": "string",
+ "description": "External ID"
+ },
+ "workflows": {
+ "$ref": "#/definitions/WorkflowOverview"
+ },
+ "_files": {
+ "$ref": "#/definitions/BaseRelation"
+ },
+ "_created_at": {
+ "readOnly": true,
+ "type": "string",
+ "format": "date-time",
+ "nullable": true,
+ "description": "Timestamp of entity creation"
+ },
+ "_updated_at": {
+ "readOnly": true,
+ "type": "string",
+ "format": "date-time",
+ "nullable": true,
+ "description": "Timestamp of entity last update"
+ }
+ },
+ "description": "contact entity (hydrated from entity graph)"
+ },
+ "contract": {
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
+ "type": "object",
+ "required": [
+ "_schema",
+ "type",
+ "billing_period",
+ "installment_amount",
+ "balance"
+ ],
+ "properties": {
+ "_schema": {
+ "readOnly": true,
+ "type": "string",
+ "pattern": "^[a-zA-Z0-9_-]+$",
+ "example": "contract",
+ "description": "URL-friendly identifier for the entity schema"
+ },
+ "_id": {
+ "readOnly": true,
+ "type": "string",
+ "format": "uuid",
+ "example": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
+ "description": "Unique identifier for the entity"
+ },
+ "_org": {
+ "type": "string",
+ "description": "Organization Id the entity belongs to",
+ "readOnly": true,
+ "example": "123"
+ },
+ "_title": {
+ "readOnly": true,
+ "type": "string",
+ "nullable": true,
+ "example": "John Doe",
+ "description": "Generated title for the entity based on schema title_template"
+ },
+ "_tags": {
+ "type": "array",
+ "nullable": true,
+ "items": {
+ "type": "string"
+ },
+ "example": [
+ "family:label"
+ ],
+ "description": "Entity Labels"
+ },
+ "_purpose": {
+ "$ref": "#/definitions/PurposeAttribute",
+ "description": "Purposes"
+ },
+ "contract_name": {
+ "type": "string",
+ "description": "Contract Name"
+ },
+ "contract_number": {
+ "type": "string",
+ "description": "Contract Number"
+ },
+ "assignee": {
+ "$ref": "#/definitions/User",
+ "description": "Assignee"
+ },
+ "payment": {
+ "$ref": "#/definitions/BaseRelationRef",
+ "description": "Payment Details"
+ },
+ "status": {
+ "type": "string",
+ "enum": [
+ "draft",
+ "in_approval_process",
+ "approved",
+ "active",
+ "deactivated",
+ "revoked",
+ "terminated",
+ "expired"
+ ],
+ "description": "Status"
+ },
+ "description": {
+ "type": "string",
+ "description": "Description"
+ },
+ "billing_account": {
+ "$ref": "#/definitions/BaseRelation",
+ "description": "Billing Account"
+ },
+ "account_number": {
+ "type": "string",
+ "description": "Account Number"
+ },
+ "branch": {
+ "type": "string",
+ "enum": [
+ "power",
+ "gas",
+ "water",
+ "waste_water",
+ "district_heating"
+ ],
+ "description": "Branch"
+ },
+ "move_in_date": {
+ "type": "string",
+ "format": "date",
+ "example": "2025-01-01",
+ "description": "Move In Date"
+ },
+ "move_out_date": {
+ "type": "string",
+ "format": "date",
+ "example": "2025-01-01",
+ "description": "Move Out Date"
+ },
+ "billing_address": {
+ "$ref": "#/definitions/BaseRelationRef",
+ "description": "Billing Address"
+ },
+ "delivery_address": {
+ "type": "array",
+ "items": {
+ "type": "object",
+ "description": "Address attribute",
+ "additionalProperties": true,
+ "properties": {
+ "_tags": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ },
+ "example": [
+ "billing"
+ ]
+ },
+ "street": {
+ "type": "string",
+ "description": "The first line of the address. Typically the street address or PO Box number.",
+ "nullable": true,
+ "example": "Hauptstraße"
+ },
+ "street_number": {
+ "type": "string",
+ "description": "The second line of the address. Typically the number of the apartment, suite, or unit.",
+ "nullable": true,
+ "example": "123"
+ },
+ "postal_code": {
+ "type": "string",
+ "description": "The postal code for the address.",
+ "nullable": true,
+ "example": "50668"
+ },
+ "city": {
+ "type": "string",
+ "description": "The name of the city, district, village, or town.",
+ "nullable": true,
+ "example": "Cologne"
+ },
+ "country": {
+ "type": "string",
+ "description": "The two-letter code for the country of the address.",
+ "nullable": true,
+ "example": "DE"
+ },
+ "additional_info": {
+ "type": "string",
+ "description": "An additional description for the address",
+ "nullable": true
+ },
+ "company_name": {
+ "type": "string",
+ "description": "the company name, usually used as extra delivery instructions",
+ "nullable": true
+ },
+ "first_name": {
+ "type": "string",
+ "description": "the first name of the recipient, usually used as extra delivery instructions",
+ "nullable": true
+ },
+ "last_name": {
+ "type": "string",
+ "description": "the last name of the recipient, usually used as extra delivery instructions",
+ "nullable": true
+ },
+ "salutation": {
+ "type": "string",
+ "description": "the salutation of the recipient, usually used as extra delivery instructions",
+ "nullable": true
+ },
+ "title": {
+ "type": "string",
+ "description": "the title of the recipient, usually used as extra delivery instructions",
+ "nullable": true
+ },
+ "coordinates": {
+ "type": "string",
+ "description": "Geographical coordinates (latitude, longitude)",
+ "nullable": true,
+ "example": "50.948484, 6.943147"
+ },
+ "plot_of_land": {
+ "type": "string",
+ "description": "Plot of land information",
+ "nullable": true
+ },
+ "plot_area": {
+ "type": "string",
+ "description": "Area of the plot",
+ "nullable": true
+ },
+ "start_date": {
+ "type": "string",
+ "description": "Start date for the address",
+ "format": "date",
+ "nullable": true
+ },
+ "end_date": {
+ "type": "string",
+ "description": "End date for the address",
+ "format": "date",
+ "nullable": true
+ }
+ }
+ },
+ "description": "Delivery Address"
+ },
+ "additional_addresses": {
+ "$ref": "#/definitions/BaseRelationRef",
+ "description": "Additional Addresses"
+ },
+ "termination_date": {
+ "type": "string",
+ "format": "date",
+ "example": "2025-01-01",
+ "description": "Date of Termination"
+ },
+ "termination_reason": {
+ "type": "string",
+ "description": "Reason For Termination"
+ },
+ "start_date": {
+ "type": "string",
+ "format": "date",
+ "example": "2025-01-01",
+ "description": "Start Date"
+ },
+ "end_date": {
+ "type": "string",
+ "format": "date",
+ "example": "2025-01-01",
+ "description": "End Date"
+ },
+ "customer": {
+ "$ref": "#/definitions/BaseRelation",
+ "description": "Customer"
+ },
+ "order": {
+ "$ref": "#/definitions/BaseRelation",
+ "description": "Order"
+ },
+ "type": {
+ "type": "string",
+ "enum": [
+ "one_time",
+ "recurring"
+ ],
+ "description": "Type"
+ },
+ "billing_period": {
+ "type": "string",
+ "enum": [
+ "weekly",
+ "monthly",
+ "every_quarter",
+ "every_6_months",
+ "yearly"
+ ],
+ "description": "Billing Period"
+ },
+ "billing_duration_amount": {
+ "type": "string",
+ "description": "Billing Duration"
+ },
+ "billing_duration_unit": {
+ "type": "string",
+ "enum": [
+ "weeks",
+ "months",
+ "years"
+ ],
+ "description": "Billing Duration Unit"
+ },
+ "notice_time_amount": {
+ "type": "string",
+ "description": "Notice Time"
+ },
+ "notice_time_unit": {
+ "type": "string",
+ "enum": [
+ "weeks",
+ "months",
+ "years"
+ ],
+ "description": "Notice Time Unit"
+ },
+ "termination_time_amount": {
+ "type": "string",
+ "description": "Termination Time"
+ },
+ "termination_time_unit": {
+ "type": "string",
+ "enum": [
+ "weeks",
+ "months",
+ "years"
+ ],
+ "description": "Termination Time Unit"
+ },
+ "renewal_duration_amount": {
+ "type": "string",
+ "description": "Renewal Duration"
+ },
+ "renewal_duration_unit": {
+ "type": "string",
+ "enum": [
+ "weeks",
+ "months",
+ "years"
+ ],
+ "description": "Renewal Duration Unit"
+ },
+ "billing_due_day": {
+ "type": "string",
+ "description": "Billing Due Day"
+ },
+ "installment_amount_currency": {
+ "type": "string",
+ "description": "Three-letter ISO currency code, in lowercase. Must be a supported currency.",
+ "example": "EUR"
+ },
+ "installment_amount_decimal": {
+ "type": "string",
+ "description": "The currency value as a decimal string",
+ "example": "123.45"
+ },
+ "installment_amount": {
+ "type": "number",
+ "description": "Installment Currency"
+ },
+ "balance_currency": {
+ "type": "string",
+ "description": "Three-letter ISO currency code, in lowercase. Must be a supported currency.",
+ "example": "EUR"
+ },
+ "balance_decimal": {
+ "type": "string",
+ "description": "The currency value as a decimal string",
+ "example": "123.45"
+ },
+ "balance": {
+ "type": "number",
+ "description": "Currency"
+ },
+ "meters": {
+ "$ref": "#/definitions/BaseRelation",
+ "description": "Meters"
+ },
+ "last_sync_at": {
+ "type": "string",
+ "format": "date",
+ "example": "2025-01-01",
+ "description": "Last Sync At"
+ },
+ "external_id": {
+ "type": "string",
+ "description": "External ID"
+ },
+ "workflows": {
+ "$ref": "#/definitions/WorkflowOverview"
+ },
+ "line_items": {
+ "$ref": "#/definitions/LineItems"
+ },
+ "total_details": {
+ "$ref": "#/definitions/TotalDetails"
+ },
+ "_files": {
+ "$ref": "#/definitions/BaseRelation"
+ },
+ "_created_at": {
+ "readOnly": true,
+ "type": "string",
+ "format": "date-time",
+ "nullable": true,
+ "description": "Timestamp of entity creation"
+ },
+ "_updated_at": {
+ "readOnly": true,
+ "type": "string",
+ "format": "date-time",
+ "nullable": true,
+ "description": "Timestamp of entity last update"
+ }
+ },
+ "description": "contract entity (hydrated from entity graph)"
+ },
+ "billing_account": {
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
+ "type": "object",
+ "required": [
+ "_schema"
+ ],
+ "properties": {
+ "_schema": {
+ "readOnly": true,
+ "type": "string",
+ "pattern": "^[a-zA-Z0-9_-]+$",
+ "example": "billing_account",
+ "description": "URL-friendly identifier for the entity schema"
+ },
+ "_id": {
+ "readOnly": true,
+ "type": "string",
+ "format": "uuid",
+ "example": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
+ "description": "Unique identifier for the entity"
+ },
+ "_org": {
+ "type": "string",
+ "description": "Organization Id the entity belongs to",
+ "readOnly": true,
+ "example": "123"
+ },
+ "_title": {
+ "readOnly": true,
+ "type": "string",
+ "nullable": true,
+ "example": "John Doe",
+ "description": "Generated title for the entity based on schema title_template"
+ },
+ "_tags": {
+ "type": "array",
+ "nullable": true,
+ "items": {
+ "type": "string"
+ },
+ "example": [
+ "family:label"
+ ],
+ "description": "Entity Labels"
+ },
+ "billing_account_number": {
+ "type": "string",
+ "description": "Billing Account Number"
+ },
+ "balance_currency": {
+ "type": "string",
+ "description": "Three-letter ISO currency code, in lowercase. Must be a supported currency.",
+ "example": "EUR"
+ },
+ "balance_decimal": {
+ "type": "string",
+ "description": "The currency value as a decimal string",
+ "example": "123.45"
+ },
+ "balance": {
+ "type": "number",
+ "description": "Account Balance"
+ },
+ "billing_contact": {
+ "$ref": "#/definitions/BaseRelation",
+ "description": "Billing Contact"
+ },
+ "billing_address": {
+ "$ref": "#/definitions/AddressAttribute",
+ "description": "Billing Address"
+ },
+ "payment_method": {
+ "type": "array",
+ "items": {
+ "type": "object",
+ "description": "Payment method details",
+ "properties": {
+ "type": {
+ "type": "string",
+ "enum": [
+ "payment_sepa",
+ "payment_invoice",
+ "payment_cash"
+ ],
+ "example": "payment_sepa"
+ },
+ "data": {
+ "type": "object",
+ "properties": {
+ "bank_name": {
+ "type": "string",
+ "example": "Deutsche Bank"
+ },
+ "iban": {
+ "type": "string",
+ "example": "DE89370400440532013000"
+ },
+ "bic_number": {
+ "type": "string",
+ "example": "DEUTDEFF"
+ },
+ "fullname": {
+ "type": "string",
+ "example": "John Doe"
+ }
+ }
+ }
+ }
+ },
+ "description": "Payment Method"
+ },
+ "contracts": {
+ "$ref": "#/definitions/BaseRelation",
+ "description": "Contracts"
+ },
+ "external_id": {
+ "type": "string",
+ "description": "External ID"
+ },
+ "workflows": {
+ "$ref": "#/definitions/WorkflowOverview"
+ },
+ "_files": {
+ "$ref": "#/definitions/BaseRelation"
+ },
+ "_created_at": {
+ "readOnly": true,
+ "type": "string",
+ "format": "date-time",
+ "nullable": true,
+ "description": "Timestamp of entity creation"
+ },
+ "_updated_at": {
+ "readOnly": true,
+ "type": "string",
+ "format": "date-time",
+ "nullable": true,
+ "description": "Timestamp of entity last update"
+ }
+ },
+ "description": "billing_account entity (hydrated from entity graph)"
+ },
+ "ticket_files": {
+ "type": "array",
+ "description": "Array of file entities (hydrated from entity graph)",
+ "items": {
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
+ "type": "object",
+ "required": [
+ "_schema",
+ "filename",
+ "language"
+ ],
+ "properties": {
+ "_schema": {
+ "readOnly": true,
+ "type": "string",
+ "pattern": "^[a-zA-Z0-9_-]+$",
+ "example": "file",
+ "description": "URL-friendly identifier for the entity schema"
+ },
+ "_id": {
+ "readOnly": true,
+ "type": "string",
+ "format": "uuid",
+ "example": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
+ "description": "Unique identifier for the entity"
+ },
+ "_org": {
+ "type": "string",
+ "description": "Organization Id the entity belongs to",
+ "readOnly": true,
+ "example": "123"
+ },
+ "_title": {
+ "readOnly": true,
+ "type": "string",
+ "nullable": true,
+ "example": "John Doe",
+ "description": "Generated title for the entity based on schema title_template"
+ },
+ "_tags": {
+ "type": "array",
+ "nullable": true,
+ "items": {
+ "type": "string"
+ },
+ "example": [
+ "family:label"
+ ],
+ "description": "Entity Labels"
+ },
+ "_purpose": {
+ "$ref": "#/definitions/PurposeAttribute",
+ "description": "Purposes"
+ },
+ "_thumbnail": {
+ "$ref": "#/definitions/BaseRelation",
+ "description": "Thumbnail"
+ },
+ "filename": {
+ "type": "string",
+ "description": "File Name"
+ },
+ "mime_type": {
+ "type": "string",
+ "description": "MIME Type"
+ },
+ "type": {
+ "type": "string",
+ "enum": [
+ "document",
+ "document_template",
+ "text",
+ "image",
+ "video",
+ "audio",
+ "spreadsheet",
+ "presentation",
+ "font",
+ "archive",
+ "application",
+ "unknown"
+ ],
+ "description": "File Type"
+ },
+ "readable_size": {
+ "type": "string",
+ "description": "Size"
+ },
+ "language": {
+ "type": "string",
+ "enum": [
+ "de",
+ "en"
+ ],
+ "description": "Language"
+ },
+ "alt_text": {
+ "type": "string",
+ "description": "Alt text"
+ },
+ "access_control": {
+ "type": "string",
+ "enum": [
+ "private",
+ "public-read"
+ ],
+ "description": "Access Control"
+ },
+ "public_url": {
+ "type": "string",
+ "description": "Public URL"
+ },
+ "shared_with_end_customer": {
+ "type": "boolean",
+ "description": "Shared with End Customer"
+ },
+ "category": {
+ "type": "string",
+ "enum": [
+ "address-suggestions",
+ "unknown"
+ ],
+ "description": "Category"
+ },
+ "_files": {
+ "$ref": "#/definitions/BaseRelation"
+ },
+ "_created_at": {
+ "readOnly": true,
+ "type": "string",
+ "format": "date-time",
+ "nullable": true,
+ "description": "Timestamp of entity creation"
+ },
+ "_updated_at": {
+ "readOnly": true,
+ "type": "string",
+ "format": "date-time",
+ "nullable": true,
+ "description": "Timestamp of entity last update"
+ }
+ }
+ }
+ }
+ },
+ "required": [
+ "_org_id",
+ "_event_time",
+ "_event_id",
+ "_event_name",
+ "_event_version",
+ "_event_source",
+ "_ack_id",
+ "event_attachments",
+ "ticket",
+ "_trigger_source_type",
+ "_trigger_source"
+ ],
+ "additionalProperties": true,
+ "definitions": {
+ "PurposeAttribute": {
+ "type": "array",
+ "description": "List of purpose ids",
+ "items": {
+ "type": "string"
+ },
+ "example": [
+ "7675dc56-fd1b-46ee-9c35-defe0667b32e"
+ ]
+ },
+ "LinkAttribute": {
+ "type": "object",
+ "description": "Link with title and href",
+ "properties": {
+ "type": {
+ "type": "string",
+ "enum": [
+ "link"
+ ]
+ },
+ "title": {
+ "type": "string",
+ "example": "Visit Website"
+ },
+ "href": {
+ "type": "string",
+ "format": "uri",
+ "example": "https://example.com"
+ }
+ }
+ },
+ "BaseUUID": {
+ "type": "string",
+ "format": "uuid",
+ "example": "123e4567-e89b-12d3-a456-426614174000"
+ },
+ "BaseTags": {
+ "type": "array",
+ "nullable": true,
+ "items": {
+ "type": "string"
+ },
+ "example": [
+ "example"
+ ]
+ },
+ "BaseRelation": {
+ "type": "object",
+ "nullable": true,
+ "description": "Relation to another entity",
+ "properties": {
+ "$relation": {
+ "type": "array",
+ "items": {
+ "type": "object",
+ "properties": {
+ "entity_id": {
+ "$ref": "#/definitions/BaseUUID"
+ },
+ "_tags": {
+ "$ref": "#/definitions/BaseTags"
+ }
+ }
+ }
+ }
+ }
+ },
+ "WorkflowStatus": {
+ "type": "string",
+ "enum": [
+ "STARTED",
+ "DONE",
+ "CLOSED"
+ ]
+ },
+ "StepType": {
+ "type": "string",
+ "enum": [
+ "MANUAL",
+ "AUTOMATION"
+ ]
+ },
+ "StepStatus": {
+ "type": "string",
+ "enum": [
+ "UNASSIGNED",
+ "ASSIGNED",
+ "COMPLETED",
+ "SKIPPED",
+ "IN_PROGRESS"
+ ]
+ },
+ "PhaseInEntity": {
+ "type": "object",
+ "description": "Workflow phase information",
+ "properties": {
+ "phase_id": {
+ "type": "string"
+ },
+ "phase_name": {
+ "type": "string"
+ },
+ "phase_progress": {
+ "type": "number"
+ }
+ },
+ "required": [
+ "phase_id",
+ "phase_name",
+ "phase_progress"
+ ]
+ },
+ "WorkflowInEntity": {
+ "type": "object",
+ "properties": {
+ "id": {
+ "type": "string"
+ },
+ "definition_id": {
+ "type": "string"
+ },
+ "name": {
+ "type": "string"
+ },
+ "status": {
+ "$ref": "#/definitions/WorkflowStatus"
+ },
+ "assignees": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "progress": {
+ "type": "number"
+ },
+ "phases_in_progress": {
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/PhaseInEntity"
+ }
+ },
+ "last_update_time": {
+ "type": "string",
+ "format": "date-time"
+ },
+ "upcoming_tasks_assignees": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "task_id": {
+ "type": "string"
+ },
+ "task_name": {
+ "type": "string"
+ },
+ "task_assignees": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "task_duedate": {
+ "type": "string",
+ "format": "date-time"
+ },
+ "task_execution_type": {
+ "$ref": "#/definitions/StepType"
+ },
+ "task_status": {
+ "$ref": "#/definitions/StepStatus"
+ },
+ "phase_id": {
+ "type": "string"
+ },
+ "phase_name": {
+ "type": "string"
+ },
+ "phase_assignees": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "phase_progress": {
+ "type": "number"
+ }
+ },
+ "required": [
+ "id",
+ "definition_id",
+ "name",
+ "status",
+ "assignees",
+ "last_update_time"
+ ]
+ },
+ "WorkflowOverview": {
+ "type": "object",
+ "properties": {
+ "primary": {
+ "$ref": "#/definitions/WorkflowInEntity"
+ }
+ },
+ "additionalProperties": {
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/WorkflowInEntity"
+ }
+ }
+ },
+ "ConsentAttribute": {
+ "type": "object",
+ "description": "Consent Management",
+ "properties": {
+ "type": {
+ "type": "string",
+ "enum": [
+ "consent"
+ ]
+ },
+ "topic": {
+ "type": "string"
+ },
+ "identifiers": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ }
+ },
+ "required": [
+ "type",
+ "topic"
+ ]
+ },
+ "UserId": {
+ "description": "User's unique identifier",
+ "type": "string",
+ "readOnly": true
+ },
+ "OrganizationId": {
+ "description": "Organization unique identifier",
+ "type": "string",
+ "readOnly": true,
+ "example": "123"
+ },
+ "User": {
+ "type": "object",
+ "description": "User information",
+ "properties": {
+ "id": {
+ "$ref": "#/definitions/UserId"
+ },
+ "organization_id": {
+ "$ref": "#/definitions/OrganizationId"
+ },
+ "email": {
+ "type": "string",
+ "format": "email",
+ "example": "user@example.com"
+ },
+ "display_name": {
+ "type": "string",
+ "example": "John Doe",
+ "deprecated": true
+ },
+ "name": {
+ "type": "string",
+ "example": "John Doe",
+ "deprecated": true
+ },
+ "preferred_language": {
+ "type": "string",
+ "example": "de",
+ "description": "ISO 639-1 language code"
+ },
+ "signature": {
+ "type": "string",
+ "example": "Best regards,
John Doe
",
+ "description": "HTML email signature"
+ },
+ "roles": {
+ "type": "array",
+ "items": {
+ "type": "string",
+ "example": "Owner"
+ },
+ "deprecated": true,
+ "description": "Deprecated! Please use Permissions API instead"
+ },
+ "image_uri": {
+ "type": "object",
+ "additionalProperties": true,
+ "properties": {
+ "original": {
+ "type": "string",
+ "format": "uri",
+ "example": "https://account-profile-images.epilot.cloud/1/avatar.png"
+ },
+ "thumbnail_32": {
+ "type": "string",
+ "format": "uri",
+ "example": "https://account-profile-images.epilot.cloud/1/avatar_32x32.png"
+ }
+ },
+ "nullable": true
+ },
+ "properties": {
+ "type": "array",
+ "items": {
+ "type": "object",
+ "properties": {
+ "name": {
+ "type": "string",
+ "example": "profileImageName"
+ },
+ "value": {
+ "type": "string",
+ "example": "avatar.png"
+ }
+ },
+ "required": [
+ "name",
+ "value"
+ ]
+ }
+ }
+ },
+ "required": [
+ "id",
+ "organization_id",
+ "email",
+ "name",
+ "preferred_language",
+ "roles",
+ "properties"
+ ]
+ },
+ "BaseRelationRef": {
+ "type": "object",
+ "nullable": true,
+ "description": "Reference to a specific property of another entity",
+ "properties": {
+ "$relation_ref": {
+ "type": "array",
+ "items": {
+ "type": "object",
+ "properties": {
+ "entity_id": {
+ "$ref": "#/definitions/BaseUUID"
+ },
+ "path": {
+ "type": "string",
+ "example": "address",
+ "description": "The property of the entity (pointed by entity_id) to be used as the reference"
+ },
+ "_tags": {
+ "$ref": "#/definitions/BaseTags"
+ },
+ "_id": {
+ "type": "string",
+ "example": "xHcOoJCa07eysJ1GaQeSb"
+ }
+ }
+ }
+ }
+ }
+ },
+ "TaxBreakdownInfo": {
+ "type": "object",
+ "properties": {
+ "rate": {
+ "type": "number"
+ },
+ "type": {
+ "type": "string",
+ "enum": [
+ "VAT",
+ "GST",
+ "Custom"
+ ]
+ },
+ "_id": {
+ "type": "string"
+ }
+ }
+ },
+ "TaxAmountBreakdown": {
+ "type": "object",
+ "description": "A tax amount associated with a specific tax rate.",
+ "readOnly": true,
+ "properties": {
+ "amount": {
+ "type": "integer"
+ },
+ "rate": {
+ "type": "string",
+ "description": "The tax rate applied. With the release of the tax manager feature this field is being deprecated in favor of the tax field.",
+ "deprecated": true
+ },
+ "rateValue": {
+ "type": "number",
+ "description": "The tax rate value applied. With the release of the tax manager feature this field is being deprecated in favor of the tax field.",
+ "deprecated": true
+ },
+ "tax": {
+ "$ref": "#/definitions/TaxBreakdownInfo"
+ }
+ }
+ },
+ "Amounts": {
+ "type": "object",
+ "properties": {
+ "amount_subtotal": {
+ "type": "integer"
+ },
+ "amount_subtotal_decimal": {
+ "type": "string"
+ },
+ "amount_total": {
+ "type": "integer"
+ },
+ "amount_total_decimal": {
+ "type": "string"
+ }
+ }
+ },
+ "Currency": {
+ "type": "string",
+ "description": "Three-letter ISO currency code, in lowercase. Must be a supported currency.",
+ "example": "EUR"
+ },
+ "RecurrenceAmount": {
+ "type": "object",
+ "description": "An amount associated with a specific recurrence.",
+ "readOnly": true,
+ "allOf": [
+ {
+ "$ref": "#/definitions/Amounts"
+ }
+ ],
+ "properties": {
+ "type": {
+ "type": "string"
+ },
+ "billing_period": {
+ "type": "string",
+ "enum": [
+ "weekly",
+ "monthly",
+ "every_quarter",
+ "every_6_months",
+ "yearly"
+ ]
+ },
+ "unit_amount_gross": {
+ "type": "integer"
+ },
+ "unit_amount_net": {
+ "type": "integer"
+ },
+ "amount_tax": {
+ "type": "integer"
+ },
+ "currency": {
+ "$ref": "#/definitions/Currency"
+ }
+ },
+ "required": [
+ "amount_subtotal",
+ "amount_total",
+ "amount_subtotal_decimal",
+ "amount_total_decimal"
+ ]
+ },
+ "RecurrenceAmountWithTax": {
+ "type": "object",
+ "description": "An amount associated with a specific recurrence.",
+ "properties": {
+ "type": {
+ "type": "string"
+ },
+ "billing_period": {
+ "type": "string",
+ "enum": [
+ "weekly",
+ "monthly",
+ "every_quarter",
+ "every_6_months",
+ "yearly"
+ ]
+ },
+ "amount_total": {
+ "type": "integer"
+ },
+ "amount_subtotal": {
+ "type": "integer"
+ },
+ "amount_tax": {
+ "type": "integer"
+ },
+ "tax": {
+ "$ref": "#/definitions/TaxAmountBreakdown"
+ }
+ },
+ "required": [
+ "amount_subtotal",
+ "amount_total"
+ ]
+ },
+ "TotalDetails": {
+ "type": "object",
+ "description": "The total details with tax (and discount) aggregated totals.",
+ "readOnly": true,
+ "properties": {
+ "amount_shipping": {
+ "type": "integer",
+ "description": "This is the sum of all the price item shipping amounts."
+ },
+ "amount_tax": {
+ "type": "integer",
+ "description": "This is the sum of all the price item tax amounts."
+ },
+ "breakdown": {
+ "type": "object",
+ "description": "Breakdown of individual tax (and discount) amounts that add up to the totals.",
+ "properties": {
+ "taxes": {
+ "type": "array",
+ "description": "The aggregated price items tax amount per rate.",
+ "items": {
+ "$ref": "#/definitions/TaxAmountBreakdown"
+ }
+ },
+ "recurrences": {
+ "type": "array",
+ "description": "The aggregated price items tax amount per rate.",
+ "items": {
+ "$ref": "#/definitions/RecurrenceAmount"
+ }
+ },
+ "recurrencesByTax": {
+ "type": "array",
+ "description": "The aggregated price items recurrences by tax rate",
+ "items": {
+ "$ref": "#/definitions/RecurrenceAmountWithTax"
+ }
+ }
+ }
+ }
+ }
+ },
+ "LineItems": {
+ "type": "array",
+ "items": {
+ "type": "object",
+ "properties": {
+ "price_id": {
+ "$ref": "#/definitions/BaseUUID"
+ },
+ "product_id": {
+ "$ref": "#/definitions/BaseUUID"
+ },
+ "quantity": {
+ "type": "integer"
+ },
+ "description": {
+ "type": "string"
+ },
+ "unit_amount": {
+ "type": "integer"
+ },
+ "unit_amount_decimal": {
+ "type": "string"
+ },
+ "pricing_model": {
+ "type": "string",
+ "enum": [
+ "per_unit"
+ ]
+ },
+ "_price": {
+ "type": "object",
+ "properties": {
+ "_id": {
+ "$ref": "#/definitions/BaseUUID"
+ },
+ "_title": {
+ "type": "string"
+ },
+ "description": {
+ "type": "string"
+ },
+ "unit_amount": {
+ "type": "integer"
+ },
+ "unit_amount_decimal": {
+ "type": "string"
+ },
+ "unit_amount_currency": {
+ "type": "string"
+ },
+ "active": {
+ "type": "boolean"
+ },
+ "sales_tax": {
+ "type": "string"
+ },
+ "tax_behavior": {
+ "type": "string"
+ },
+ "price_display_in_journeys": {
+ "type": "string"
+ },
+ "type": {
+ "type": "string"
+ },
+ "billing_period": {
+ "type": "string"
+ },
+ "billing_duration_unit": {
+ "type": "string"
+ },
+ "notice_time_unit": {
+ "type": "string"
+ },
+ "termination_time_unit": {
+ "type": "string"
+ },
+ "renewal_duration_unit": {
+ "type": "string"
+ },
+ "internal_description": {
+ "type": "string"
+ },
+ "pricing_model": {
+ "type": "string"
+ },
+ "is_tax_inclusive": {
+ "type": "boolean"
+ },
+ "headline": {
+ "type": "string"
+ },
+ "subHeadline": {
+ "type": "string"
+ },
+ "group": {
+ "type": "string"
+ }
+ }
+ },
+ "_product": {
+ "type": "object",
+ "properties": {
+ "type": {
+ "type": "string",
+ "enum": [
+ "product"
+ ]
+ },
+ "_title": {
+ "type": "string"
+ },
+ "name": {
+ "type": "string"
+ },
+ "description": {
+ "type": "string"
+ },
+ "price_options": {
+ "type": "array",
+ "items": {
+ "type": "object",
+ "properties": {
+ "_id": {
+ "$ref": "#/definitions/BaseUUID"
+ },
+ "_title": {
+ "type": "string"
+ },
+ "description": {
+ "type": "string"
+ },
+ "unit_amount": {
+ "type": "integer"
+ },
+ "unit_amount_decimal": {
+ "type": "string"
+ },
+ "unit_amount_currency": {
+ "type": "string"
+ },
+ "active": {
+ "type": "boolean"
+ },
+ "_schema": {
+ "type": "string"
+ },
+ "_org": {
+ "type": "string"
+ },
+ "_created_at": {
+ "type": "string",
+ "format": "date-time"
+ },
+ "_updated_at": {
+ "type": "string",
+ "format": "date-time"
+ },
+ "sales_tax": {
+ "type": "string"
+ },
+ "tax_behavior": {
+ "type": "string"
+ },
+ "price_display_in_journeys": {
+ "type": "string"
+ },
+ "type": {
+ "type": "string"
+ },
+ "billing_period": {
+ "type": "string"
+ },
+ "billing_duration_unit": {
+ "type": "string"
+ },
+ "notice_time_unit": {
+ "type": "string"
+ },
+ "termination_time_unit": {
+ "type": "string"
+ },
+ "renewal_duration_unit": {
+ "type": "string"
+ },
+ "internal_description": {
+ "type": "string"
+ },
+ "_owners": {
+ "type": "array",
+ "items": {
+ "type": "object",
+ "properties": {
+ "org_id": {
+ "type": "string"
+ }
+ }
+ }
+ },
+ "pricing_model": {
+ "type": "string"
+ },
+ "is_tax_inclusive": {
+ "type": "boolean"
+ },
+ "_acl": {
+ "type": "object",
+ "properties": {
+ "view": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "edit": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "delete": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ }
+ }
+ },
+ "$relation": {
+ "type": "object",
+ "properties": {
+ "entity_id": {
+ "$ref": "#/definitions/BaseUUID"
+ },
+ "_schema": {
+ "type": "string"
+ },
+ "_tags": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "_id": {
+ "$ref": "#/definitions/BaseUUID"
+ }
+ }
+ },
+ "taxes": {
+ "type": "array",
+ "items": {
+ "type": "object",
+ "properties": {
+ "rate": {
+ "type": "string"
+ },
+ "rateValue": {
+ "type": "integer"
+ },
+ "amount": {
+ "type": "integer"
+ }
+ }
+ }
+ },
+ "currency": {
+ "type": "string"
+ },
+ "unit_amount_net": {
+ "type": "integer"
+ },
+ "unit_amount_net_decimal": {
+ "type": "string"
+ },
+ "unit_amount_gross": {
+ "type": "integer"
+ },
+ "unit_amount_gross_decimal": {
+ "type": "string"
+ },
+ "amount_subtotal": {
+ "type": "integer"
+ },
+ "amount_total": {
+ "type": "integer"
+ },
+ "amount_tax": {
+ "type": "integer"
+ },
+ "is_tax_inclusive": {
+ "type": "boolean"
+ },
+ "amount_subtotal_decimal": {
+ "type": "string"
+ },
+ "amount_total_decimal": {
+ "type": "string"
+ }
+ }
+ }
+ },
+ "AddressAttribute": {
+ "type": "object",
+ "description": "Address attribute",
+ "additionalProperties": true,
+ "properties": {
+ "_tags": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ },
+ "example": [
+ "billing"
+ ]
+ },
+ "street": {
+ "type": "string",
+ "description": "The first line of the address. Typically the street address or PO Box number.",
+ "nullable": true,
+ "example": "Hauptstraße"
+ },
+ "street_number": {
+ "type": "string",
+ "description": "The second line of the address. Typically the number of the apartment, suite, or unit.",
+ "nullable": true,
+ "example": "123"
+ },
+ "postal_code": {
+ "type": "string",
+ "description": "The postal code for the address.",
+ "nullable": true,
+ "example": "50668"
+ },
+ "city": {
+ "type": "string",
+ "description": "The name of the city, district, village, or town.",
+ "nullable": true,
+ "example": "Cologne"
+ },
+ "country": {
+ "type": "string",
+ "description": "The two-letter code for the country of the address.",
+ "nullable": true,
+ "example": "DE"
+ },
+ "additional_info": {
+ "type": "string",
+ "description": "An additional description for the address",
+ "nullable": true
+ },
+ "company_name": {
+ "type": "string",
+ "description": "the company name, usually used as extra delivery instructions",
+ "nullable": true
+ },
+ "first_name": {
+ "type": "string",
+ "description": "the first name of the recipient, usually used as extra delivery instructions",
+ "nullable": true
+ },
+ "last_name": {
+ "type": "string",
+ "description": "the last name of the recipient, usually used as extra delivery instructions",
+ "nullable": true
+ },
+ "salutation": {
+ "type": "string",
+ "description": "the salutation of the recipient, usually used as extra delivery instructions",
+ "nullable": true
+ },
+ "title": {
+ "type": "string",
+ "description": "the title of the recipient, usually used as extra delivery instructions",
+ "nullable": true
+ },
+ "coordinates": {
+ "type": "string",
+ "description": "Geographical coordinates (latitude, longitude)",
+ "nullable": true,
+ "example": "50.948484, 6.943147"
+ },
+ "plot_of_land": {
+ "type": "string",
+ "description": "Plot of land information",
+ "nullable": true
+ },
+ "plot_area": {
+ "type": "string",
+ "description": "Area of the plot",
+ "nullable": true
+ },
+ "start_date": {
+ "type": "string",
+ "description": "Start date for the address",
+ "format": "date",
+ "nullable": true
+ },
+ "end_date": {
+ "type": "string",
+ "description": "End date for the address",
+ "format": "date",
+ "nullable": true
+ }
+ }
+ }
+ }
+}
\ No newline at end of file