Productsup

Replace an export template

Replaces an export template using full-replace semantics: after the operation completes, the server state matches the request payload exactly. Attributes are matched by name (natural key): an attribute in the payload whose name matches an existing attribute updates that attribute in place — it keeps its existing ID, and Platform mappings that reference it are not broken. Attributes and analyzer tests omitted from the payload are deleted. Analyzer tests are replaced per attribute: existing tests are deleted and the tests in the payload are inserted. **Always send the complete template.** This endpoint is also the right tool for small, incremental changes — for example, adding a new attribute or deprecating an existing one. Fetch the current template with `GET /V2/export-templates/{id}`, apply your change to the full payload, and send the whole template back. A partial payload deletes every attribute and analyzer test it omits. The operation runs asynchronously and returns 202 Accepted with a Content-Location header pointing to the operation resource. Poll GET /V2/operations/{operationId} for progress and the result. Returns 404 if the export template does not exist; no operation is created. **Field semantics.** Fields do not all behave the same way when you leave them out. This table is the contract: | Field | When you send it | When you omit it | | --- | --- | --- | | `name` | Replaces the name | Required, the request is rejected | | `attributes` | Replaces the whole set. Attributes are matched by name, so a matching one is updated in place and keeps its ID | Every attribute is deleted | | `tags`, `customFormFields` | Replace the whole set | The set is emptied | | `metadata`, `exportMarketing` | Replaced wholesale, so a sub-field you leave out of the object is cleared | Left untouched | | `exportType` | Wins over whatever the raw tags say | Derived from `tags` | | `global`, `accountId`, `projectId`, `siteId` | Applied, admin and internal users only | Left unchanged | | `logoUrl` | Stored verbatim | Left unchanged | | `context` | Accepted only for an active channel template context | Left unchanged | | Other scalars, for example `encoding`, `defaultFilename`, `validDelimiter`, `deltaNewFilename` | Applied | Nullable ones are reset to their default | Server-assigned ids are never written back. Strip `id` from every `attributes`, `tags`, `customFormFields` and `analyzerTests` entry, along with `systemManaged` on analyzer tests, or the request is rejected. Two fields are easy to get wrong when you build the payload from a `GET`: - `logoUrl` is owned by the dedicated logo endpoints. The read returns an absolute CDN URL while the stored value is a relative path, so echoing the read value back would persist the absolute URL. Leave it out and the stored logo is untouched. - `context` is a derived projection on read, returned as `accountIds`, `projectIds` and `siteIds` for convenience. That shape is rejected by the write, so leave it out too. **Publishing required.** This request stores a draft. The change becomes available in the Productsup platform only after you publish the export template with `POST /V2/export-templates/{id}/publish` (the legacy `POST /V1/export-templates/{id}/commit` still works for existing callers). Analyzer test assignments in the payload are the exception — they take effect immediately, without publishing. **Optimistic locking.** Clients must send the current `draft_version` in the `If-Match` header (from GET `draftVersion` / ETag). A stale or mismatched value returns 409 Conflict and applies no changes.

PUT
/V2/export-templates/{id}
AuthorizationBearer <token>

Productsup Keycloak JWT bearer token.

In: header

Path Parameters

id*integer

Export template ID

Header Parameters

If-Match*string

Current draft_version of the export template (optimistic locking). Send the value from GET draftVersion or the ETag response header. Mismatch returns 409 Conflict.

Request Body

application/json

TypeScript Definitions

Use the request body type in TypeScript.

Response Body

application/json

application/json

application/json

application/json

application/json

curl -X PUT "https://export-template-api.productsup.com/V2/export-templates/0" \  -H "If-Match: 1" \  -H "Content-Type: application/json" \  -d '{    "name": "Google Shopping Feed"  }'
{
  "operationId": "01980f10-5b04-7845-b0d5-66861c6cabb0",
  "status": "queued"
}
{
  "message": "Invalid JSON payload"
}
{
  "errors": {
    "message": "Resource access denied."
  }
}
{
  "code": "NOT_FOUND",
  "message": "Export template with ID 12345 not found."
}
{
  "errors": {
    "message": "Draft version mismatch: expected 1, current is 2. Re-fetch the export template and retry."
  }
}