Publish
Publish sends your transformed data directly to an external API — row by row — as structured JSON HTTP requests. Instead of downloading a file and importing it yourself, WeTransform delivers each row of your transformed data to the endpoint of your choice.
What is Publish?
Publish is a data delivery mechanism attached to a Template. After a transformation completes, WeTransform reads the output and sends one HTTP request per row to your server, with a fully customizable JSON body. Think of it as an automated, row-level push of your transformed data into any system with an HTTP API.
Custom JSON payloads
Build any JSON structure visually, mapping your columns to the exact shape your API expects.
Rate limiting
Built-in sliding-window throttling so you never exceed your target API's rate limits.
Detailed reports
A CSV report is generated for every push, with request/response details for each row.
How It Works
Publish operates at the Template level. Once configured, every transformation using that template will automatically push results after completion.
- A sender submits data — A file is uploaded or sent via API to a transformation that uses your template.
- Transformation runs — WeTransform applies all your rules, validations, and corrections to produce the output.
- Publish fires — For each row in the transformation output, WeTransform builds a JSON body using your configured tree, and sends an HTTP request to your endpoint.
- Report is generated — A CSV report logs every request: status code, response body, errors, and the original row data.
- Email notification (optional) — If configured, a summary email with the report attached is sent to your chosen address.
Use Cases
Feed an External Database
Push transformed product data, customer records, or inventory updates directly into your ERP, CRM, or PIM system's API.
Sync Data Between Systems
Receive supplier data, transform it to match your schema, and publish it to a downstream system — no manual import step.
Scheduled Pipelines
Combine Publish with scheduled transformations to build a fully automated ETL pipeline that runs daily without any intervention.
Third-Party Integrations
Send cleaned data to any HTTP endpoint: Zapier, Make (Integromat), n8n, or a custom microservice.
Setup
Publish is configured on a per-template basis. Navigate to your template's settings and enable the Publish feature.
1. Enable Publish
In your template settings, toggle Publish on. A disabled Publish configuration will be saved but will not fire after transformations.
2. Configure your HTTP endpoint
Provide the URL, HTTP method, headers, and optional cookies that WeTransform should use for each request. The JSON body is configured separately via the JSON Builder (see below).
- URL: The endpoint that will receive each row's data (e.g.,
https://api.example.com/products). - Method:
POST,PUT,PATCH, or any HTTP method your API requires. - Headers: Key-value pairs such as
Authorization: Bearer <token>orContent-Type: application/json. - Cookies: Optional cookies to include in the request.
3. Build your JSON payload
Use the visual JSON Builder to define the structure of each request body. Map template columns to JSON properties.
4. (Optional) Configure rate limiting
If your target API has rate limits, set the maximum number of requests per time window.
5. (Optional) Set a notification email
Provide an email address to receive a summary report after each push execution, including the push report CSV and any transformation errors.
JSON Builder
The JSON Builder lets you visually design the JSON payload that will be sent for each row. It works as a tree of nodes, where each node represents either a structural element (object, array) or a value (column reference, literal string, number, etc.).
Column references
The most powerful node type is Column. It pulls the value from the transformation output for the current row. The JSON type (string, number, boolean) is automatically inferred from the column's data type configuration.
Example: if your template has columns product_name (text), price (number), and in_stock (boolean), you can build:
JSON Builder configuration
Root (object)
|-- "name" -> Column: product_name
|-- "price" -> Column: price
|-- "available" -> Column: in_stock
|-- "source" -> String: "WeTransform"
For a row where product_name = "Widget A", price = 19.99, in_stock = true, the resulting JSON body sent to your API would be:
Resulting HTTP request body
{
"name": "Widget A",
"price": 19.99,
"available": true,
"source": "WeTransform"
}
Node Types
Each node in the JSON tree has a type that determines how its value is resolved:
| Type | Description | Example value |
|---|---|---|
object |
A JSON object. Contains child nodes as key-value pairs. | {"key": "value"} |
array |
A JSON array. Contains child nodes as ordered elements. | ["a", "b"] |
column |
Pulls the value from the named template column for the current row. The JSON type (string, number, boolean) is automatically inferred from the column's data type. | "Widget A" or 19.99 |
string |
A static string literal. | "hello" |
number |
A static numeric literal. | 42 |
boolean |
A static boolean literal. | true |
json |
A raw JSON string that will be parsed and embedded as-is. Useful for columns that contain JSON data. | {"nested": true} |
null |
A JSON null literal. |
null |
Column type inference
When using column nodes, the JSON type is determined by the template column's data type:
| Column data type | JSON type |
|---|---|
| Text, Value, URL, Email, Image | string |
| Number (standard decimal separator) | number |
| Number (custom thousands/decimal separator) | string |
| Date (most formats) | string |
| Date (year-only or Unix timestamp) | number |
| Boolean | boolean |
| JSON | json (embedded as parsed object) |
Omit if empty
Each node has an "omit if empty" option. When enabled, the node is excluded from the output
if its resolved value is null, an empty string, or an empty array. This keeps your payloads clean
by avoiding unnecessary null fields.
HTTP Settings
Configure how each request is sent to your API:
Configuration example
{
"url": "https://api.example.com/products",
"method": "POST",
"headers": {
"Authorization": "Bearer sk-abc123...",
"Content-Type": "application/json",
"X-Custom-Header": "my-value"
},
"cookies": {}
}
| Field | Required | Description |
|---|---|---|
url |
Yes | The full URL of the endpoint to receive each request. |
method |
Yes | HTTP method: GET, POST, PUT, DELETE, or PATCH. |
headers |
No | Key-value pairs for request headers. Use this for authentication tokens, content types, etc. |
cookies |
No | Key-value pairs for cookies to include in the request. |
Rate Limiting
Many APIs enforce rate limits (e.g., "max 100 requests per minute"). Publish includes a built-in sliding-window rate limiter that automatically throttles requests to stay within your configured limits.
Configuration
rate_limit_requests
Maximum number of requests allowed in the time window. Must be ≥ 1.
rate_limit_period
Time window in seconds. Must be ≥ 1.
Examples
| Requests | Period | Meaning |
|---|---|---|
| 100 | 60 | Max 100 requests per minute |
| 2400 | 30 | Max 2,400 requests per 30 seconds (burst-friendly) |
| 10 | 1 | Max 10 requests per second |
| empty | empty | No rate limiting — requests are sent as fast as possible |
When the limit is reached, Publish automatically pauses and resumes once the window allows more requests. No rows are dropped — they are simply delayed.
Reports & Email Notifications
After every push execution, a detailed CSV report is generated and stored. Each row in the report corresponds to one HTTP request.
Report columns
| Column | Description |
|---|---|
row_number | The row index from the transformation output. |
hash | An MD5 hash of the row data, useful for deduplication. |
unique_attribute_value | The value of the template's unique column (if configured). |
is_successful | true or false — whether the request returned a 2xx status. |
request_body | The JSON body that was sent. |
response_status_code | The HTTP status code returned by your server. |
response_body | The response body returned by your server. |
error_message | Error description if the request failed. |
| source columns... | Original values from the source file, included for traceability. |
Email notifications
If you provide a notification email, WeTransform sends a summary after each push execution. The email includes:
- Transformation statistics: rows in source, rows processed, rows in error, success percentage.
- Push statistics: total requests, successful requests, failed requests, success percentage.
- Attachments: the push report CSV, and (if there were transformation errors) the transformation error report CSV.
The push report is also available for download for 30 days from the submission details page.
Examples
Simple product sync
Push product data to an external catalog API:
JSON tree
Root (object)
|-- "sku" -> Column: product_sku
|-- "name" -> Column: product_name
|-- "price" -> Column: unit_price
|-- "currency" -> String: "EUR"
|-- "in_stock" -> Column: available
Output for one row
{
"sku": "PROD-001",
"name": "Ergonomic Chair",
"price": 299.99,
"currency": "EUR",
"in_stock": true
}
Nested structure with arrays
Build complex payloads with nested objects and arrays:
JSON tree
Root (object)
|-- "id" -> Column: employee_id
|-- "person" -> Object
| |-- "first" -> Column: first_name
| |-- "last" -> Column: last_name
| |-- "email" -> Column: email
|-- "tags" -> Array
| |-- [0] -> Column: department
| |-- [1] -> String: "imported"
|-- "active" -> Boolean: true
Output for one row
{
"id": "EMP-042",
"person": {
"first": "Alice",
"last": "Martin",
"email": "alice.martin@example.com"
},
"tags": ["Engineering", "imported"],
"active": true
}
Using "omit if empty"
Keep your API payloads clean by omitting fields when values are missing:
JSON tree (omit_if_empty enabled on "phone" and "notes")
Root (object)
|-- "name" -> Column: contact_name
|-- "email" -> Column: contact_email
|-- "phone" -> Column: contact_phone (omit if empty)
|-- "notes" -> Column: contact_notes (omit if empty)
If contact_phone and contact_notes are empty for a row, the output will be:
Output (clean, no nulls)
{
"name": "Bob Smith",
"email": "bob@example.com"
}