Skip to navigation

Data sync via Funnel API

Push product and content data from your own system into Etals over HTTP.
View as Markdown

A funnel is an HTTP endpoint in Etals that accepts JSON from your system and stores it as items of one content type. You decide once, in the funnel’s mapping, which JSON field goes into which Etals field. After that, your system sends data whenever something changes.

Use a funnel for an ongoing sync. For a one-off load, an import is usually quicker.

New to the round trip? Read How integrations work first. It shows where the funnel fits next to the export back to your system.

etals-video-poster:funnels-api

Before you start

You need:

  • An API token with the Create ability (and Delete if you also want to remove items). See Authorization.
  • The token must belong to a user whose role has the manage permission for Funnels (can_manage_funnels).
  • The Funnels app installed in your workspace.
  • A content type with the properties you want to fill. See Content types and Properties.

Set up a funnel

1

Install the Funnels app

In the sidebar, open My Apps > Browse Apps and click Install on Funnels. The app then appears under My Apps. Installing an app needs access to the general settings.

2

Create the funnel

Open My Apps > Funnels and click Create Funnel.

The Funnels page with the Create Funnel button and the existing funnel Nordvik Products

3

Fill in the form

Fill in the fields below and click Save Changes.

FieldRequiredWhat it does
Content typeYesThe content type the funnel writes to. Changing it clears the mapping.
Endpoint NameYesA name you recognise, for example Nordvik products. Must be unique.
Endpoint URLYesThe last part of the endpoint path, for example nordvik-products. Must be unique. The form shows the full URL under the field. Use lowercase letters, digits and hyphens.
DescriptionNoA note for your team.
Request DataNoA sample JSON body. Etals turns it into a clickable tree for mapping.
Map Request DataYes, when Request Data is setThe mapping from JSON fields to Etals fields. It must include the identifier field.
Activate EndpointNoWhen off, the endpoint answers 404 and stores nothing.
Partial ValidationNoWhen on, valid items in a request are stored even if other items fail. See Partial validation.

The Create Funnel form for Products: Endpoint Name "Nordvik products", Endpoint URL "nordvik-products" with the example URL below it, the NV-1001 sample in Request Data and Activate Endpoint on

4

Map the fields

Click a key in the tree under Map Request Data. In the Add Mapping window, pick either a system field under Select content type field or a property under Select Property, then click Save. Repeat for every field you want to store. Mapped keys show “Mapped as” and the target in the tree.

The Add Mapping window for the path description.nb: both switches off, Select content type field empty and Description No picked under Select Property

5

Copy the endpoint URL

Open the funnel. Funnel Details shows the full Endpoint URL. Click it to copy.

There is no test button. To get a sample payload into the form, send one real request from your system first, then open Edit Funnel. If the funnel has no saved sample, Request Data is pre-filled with the most recent request it received.

Mapping

Each mapping connects one path in your JSON to one target in Etals.

Paths

Paths use dot notation from the root of one item: name, description.sv, seo.metaTitle. If you send an array of items, the paths are relative to each item, and the tree is built from the first item of the sample.

Map values that sit under object keys. Do not map a single element inside an array (such as images[0]). When a property takes a list, such as options or a reference list, map the whole array.

Targets

A target is either a system field or a property.

System fields depend on the kind of content type. Most content types, including a Products content type you create in Setup, use the first row. The other two rows apply to the built-in product and category types that some integrations (such as Shopify) use. The Select content type field list in the mapping window always shows the right ones.

Content typeSystem fields
Content types you create, such as Products, Store guides or Blog postsIdentifier (required), Parent Identifier, Name, Main Image
Built-in product typeProduct Identifier (required), Product Name, Product Image
Built-in category typeCategory Identifier (required), Parent Category Identifier, Category Name

A property appears once per localization in the Select Property search. For example, Description for Swedish and Description for Norwegian are two separate targets. Each target can be used by only one path in a funnel.

Mapping options

OptionLabel in the windowEffect
OptionalWhen enabled, this field can be left blankA missing value does not fail the item. See Missing and empty values for what happens to the stored value.
Lazy comparisonWhen enabled, this field will use lazy comparison during mappingOnly affects the identifier field. Etals looks for an existing item whose identifier contains the value you sent and uses that item. If none is found, the value you sent is used as is. Use it only when your system adds a prefix or suffix that Etals does not have, and make sure the match is unambiguous.

Send data

POST https://{your-workspace}.etals.app/api/v1/funnels/{endpoint-url}
Authorization: Bearer {api-token}
Content-Type: application/json
Accept: application/json

Always send Accept: application/json. Without it, an authentication or permission failure answers with a redirect to the sign-in page or an HTML error page instead of JSON.

The body is either one item (a JSON object) or a list of items (a JSON array). Each item is checked and stored on its own.

Example

Nordvik Outdoor maps this payload to its Products content type:

[
{
"sku": "NV-1001",
"name": "Fjord Shell Jacket",
"image": "https://nordvik.example/images/nv-1001-front.jpg",
"description": {
"sv": "<p>Vattentät skaljacka för långa dagar på fjället.</p>",
"nb": "<p>Vanntett skalljakke for lange dager på fjellet.</p>",
"en": "<p>Waterproof shell jacket for long days in the mountains.</p>"
},
"color": "blue",
"activities": ["hiking", "trekking"],
"waterproof": true,
"weightGrams": 420
},
{
"sku": "NV-1002",
"name": "Granite Trail Boot",
"image": "https://nordvik.example/images/nv-1002-side.jpg",
"description": {
"sv": "<p>Stabil vandringskänga med Vibram-sula.</p>",
"nb": "<p>Stødig tursko med Vibram-såle.</p>",
"en": "<p>Sturdy hiking boot with a Vibram sole.</p>"
},
"color": "brown",
"activities": ["hiking"],
"waterproof": true,
"weightGrams": 1180
}
]
PathMapped toProperty type
skuIdentifiersystem field
nameNamesystem field
imageMain Imagesystem field (image)
description.svDescription, Swedishtext or HTML
description.nbDescription, Norwegiantext or HTML
description.enDescription, Englishtext or HTML
colorColoroptions
activitiesActivitiesoptions (multiple)
waterproofWaterproofyes/no (boolean)
weightGramsWeight (g)number (integer)
curl -X POST "https://{your-workspace}.etals.app/api/v1/funnels/nordvik-products" \
-H "Authorization: Bearer {api-token}" \
-H "Content-Type: application/json" \
-H "Accept: application/json" \
--data @nordvik-products.json

How each property type reads its value

Property typeSendNotes
Text, HTML and other text typesA stringNumbers are stored as text. An array is joined with commas. Leading and trailing spaces are trimmed. HTML is stored as sent.
Number (integer)A number or numeric stringConverted to a whole number.
Yes/no (boolean)true, false, 1, 0, "1", "0", "true" or "false"Anything else fails with The {path} must be a boolean.
JSON (json)An object, an array, or a string containing JSONAnything else fails with The {path} must be a valid JSON object.
Multiple choice, category tree and tags (options, hierarchyOptions, tags)An array of option values, or a single valueMatched against the existing options of that property localization by their option value (the code that Etals also sends back in exports), not by display name. Values that match no option are ignored. Funnels do not create new options. The item’s selection is replaced by the matched options.
Single choice (option)One option value as a string (a one-item array is also accepted)Selects the existing option with that value. A value that matches no option selects nothing. More than one value fails with The {path} must be a single value.
Media (media) and image system fieldsOne absolute URL as a stringAnything else fails with The {path} must be a valid URL. The file is downloaded while your request is open. See Images and media.
Reference (reference)The identifier of the referenced item, as a string or numberFails with The {path} must be a non-empty string identifier. if empty.
Reference list (referenceList)An array of identifiers (a single string is also accepted)Fails with The {path} must be a non-empty array of identifiers. if empty.

References

Etals finds referenced items by the identifier they were sent with through a funnel, and if that fails, by their own identifier (product number or item identifier). Identifiers that match nothing are dropped without an error. Send the referenced items before the items that point at them.

Images and media

  • One URL per mapped field. To store several images, map each one to its own media property.
  • Etals downloads the image during your request, so large batches with many new images take longer to answer.
  • Images are reused by URL. Sending the same URL again does not download it again. To replace an image, send a new URL.
  • If a download fails, the request still succeeds. For a media property, the stored value for that item is cleared. For the main image field, the current image is kept.

Localized values

A funnel has no special locale syntax. Each localization of a property is its own mapping target, so you send one JSON field per language, market, brand or website and map each field to the matching localization. The description.sv, description.nb and description.en fields in the example above show the pattern.

If you add a language later, add the new field to your payload and add one mapping for it.

Create or update

Etals matches every item by its identifier.

Built-in product type

  1. An item previously sent through a funnel with the same identifier is updated.
  2. Otherwise, an existing product with the same product number is taken over and updated (for example one created by another integration).
  3. Otherwise, a new product is created.

Content types you create (including Products made in Setup)

  1. An item previously sent through a funnel with the same identifier is updated.
  2. Otherwise, an item in the same content type with the same identifier is updated.
  3. Otherwise, a new item is created. If Name is not mapped, the identifier is used as the name.

For both:

  • An item that was deleted is restored when you send it again.
  • Properties that the funnel does not map are never touched.
  • Send each identifier once per request. If it appears twice, both copies are applied in order.

On the built-in product type, always map Product Name. If it is not mapped, the product’s name is saved as empty, also on existing products.

Parent items

On content types you create, Parent Identifier links an item to a parent item of the same content type. The parent must already exist in Etals, or come earlier in the same request. An item whose parent cannot be found is not stored, even though the request was answered with 200. Send parents first.

The built-in product type has no parent field in a funnel.

Missing and empty values

Empty strings in your JSON are treated as missing values.

MappingValue missing or empty
Not optionalThe item fails with Value not found at path {path}.
Optional, text, number or options propertyThe item is stored, and the stored value for that property is cleared (a number becomes 0, an options selection becomes empty).
Optional, media, reference, reference list, JSON or yes/no propertyThe item still fails, with the type error from the table above.
Optional system fieldThe field is left as it is.

To update only some properties of an item, do not map the others in that funnel. A second funnel with a smaller mapping is a clean way to send partial updates.

What happens after Etals answers

When the response is 200, the items have passed validation and are queued for storage. They are written to the content type shortly after, usually within seconds, depending on load. Image downloads are the only part that happens before the response.

Once the items are stored, automations that react to data changes run for the changed items, for example to write missing descriptions. The produced content can then be sent back to your system. See API export.

Responses

Funnel responses always contain status and message. Empty fields are left out.

SituationHTTP statusstatusmessage
All items valid200successData received successfully.
Some items invalid, Partial Validation on200successSome items were imported; others failed validation.
Any item invalid, Partial Validation off400errorValidation failed for one or more items.
Body empty or not valid JSON400errorInvalid JSON data provided.
The funnel’s mapping is broken400errorInvalid Funnel data mapping provided.
No funnel with that endpoint URL404errorInvalid Funnel URL.
Funnel switched off404errorFunnel is inactive.
Funnel has no content type500errorAttributable type is not set for funnel: {name}
Unexpected error500errorAn Unexpected error occurred.
Token missing or invalid401errorYou are not authorized to access this resource.
Token lacks the ability, or the user lacks the Funnels permission403errorYou are not authorized to perform this action.

Success

{
"status": "success",
"message": "Data received successfully."
}

Validation failed (Partial Validation off)

Nothing from the request is stored.

{
"status": "error",
"message": "Validation failed for one or more items.",
"errors": {
"NV-1002": {
"Main Image": "The image must be a valid URL."
}
},
"errorsWithoutIdentifier": [
{
"index": 2,
"errors": {
"Attributable Identifier": "Attributable Identifier not found at path sku"
}
}
]
}
FieldMeaning
errorsFailed items that have an identifier, keyed by identifier. Inside, each key is the system field name (such as Main Image) or the system code of the property localization, and the value is the error message.
errorsWithoutIdentifierFailed items where the identifier itself was missing. index is the item’s position in your array, starting at 0.

Partial validation

With Partial Validation on, valid items are stored and invalid ones are skipped. The response is 200 with status: success, so check for errors and errorsWithoutIdentifier in every response.

{
"status": "success",
"message": "Some items were imported; others failed validation.",
"errors": {
"NV-1002": {
"Main Image": "The image must be a valid URL."
}
},
"data": {
"successIds": ["NV-1001"]
}
}

data.successIds lists the identifiers that were accepted. It is left out when no item was accepted, and the message stays the same in that case.

Delete items

Send a DELETE request to the same endpoint with the identifiers to remove.

curl -X DELETE "https://{your-workspace}.etals.app/api/v1/funnels/nordvik-products" \
-H "Authorization: Bearer {api-token}" \
-H "Content-Type: application/json" \
-H "Accept: application/json" \
-d '{ "identifiers": ["NV-1001", "NV-1002"] }'

You can also send a list of such objects: [{ "identifiers": ["NV-1001"] }, { "identifiers": ["NV-1003"] }].

  • The token needs the Delete ability.
  • Deletes are soft deletes. The item is hidden in Etals and comes back if you send it to the funnel again.
  • On the built-in product type, any product with that product number is deleted. On content types you create, only items that came in through a funnel are deleted.
  • Identifiers that match nothing are ignored without an error.
  • Deletes happen before the response is sent.
SituationHTTP statusBody
Request processed200{"status": "success", "message": "Delete request recieved."} (spelled exactly as Etals returns it)
An object has no identifiers key400{"status": "error", "message": "One or more items were not deleted successfully.", "errors": ["Please provide identifiers in the request."]}
identifiers is not an array400Same shape, with Identifiers must be an array.

Empty bodies, unknown or inactive funnels and authorization failures answer the same way as for POST.

Request history

Every request that reaches an existing funnel is saved, including failed and delete requests. Open the funnel to see Recent Received Requests, which shows the latest 10 with their status, message, time, the sending host (taken from your Referer or Origin header, if you send one) and the full payload. Errors for items that have an identifier are stored with the request. Items that failed because the identifier itself was missing only appear in the HTTP response, so with Partial Validation on, a batch where only such items failed is listed as a success. Log the responses on your side.

Recent Received Requests for the Nordvik products funnel with a partly failed request expanded to show its two-item payload

Use it to check what Etals actually received while you build and test the sync.

Good practice

  • Batch size. There is no fixed item limit per request. Requests that include new image URLs stay open while the images download, so keep batches to a few hundred items and set a generous client timeout.
  • Retries. A 5xx or a network error can be retried with the same body. Re-sending items is safe because they are matched by identifier.
  • Order. Send referenced items and parent items before the items that point at them.
  • One owner per field. Map only the fields your system owns, so a sync does not overwrite content produced or edited in Etals.