Save (POST) Endpoints

The Loyalty API endpoints broadly fall into the following categories:

CategoryNotes
SavesAdds and updates (and sometimes deletes) of a specific record
Bulk savesAdds and updates of multiple records through a single POST
ActionsTrigger an action on a specific record
DeletesRemoving individual records

Saving information

The General POST concept

NEON is a Customer Data Platform with rich Loyalty and Engagement functionality, triggers and other Automation side effects. This is very different from many standard CRUD REST endpoints.

The Loyalty API therefore doesn't generally operate in the same way as typical REST endpoints with separate POST, PUT and PATCH behavior.

In the Loyalty API a POST operation normally creates the content if it doesn't exist and updates it if it already exists. This means you can for example safely save a Survey answer or a Competiton entry without first having to look up the entry to see if it already exist. Configuration logic will validate the POST and reject it if multiple entries weren't allowed, or fire triggers if it was saved successfully.

DELETE is not normally supported since most information is transactional and deleting information may therefore skew insight reporting.

All content in NEON is linked to an Entity, and most content is also linked to a Member. When saving content you therefore always need to pass the Entity in the url, and in most cases you need to pass in the Member in the payload.

The urls are generally the same as for Entity-based queries, but using POST instead, for example:

POST /loyalty/v1/{space}/competitions/{lookupId}/entries
POST /loyalty/v1/{space}/surveys/{lookupId}/entries
POST /loyalty/v1/{space}/forms/{lookupId}/members
POST /loyalty/v1/{space}/ordersets/{lookupId}/orders
POST /loyalty/v1/{space}/ordersets/{lookupId}/payments
POST /loyalty/v1/{space}/lists/{lookupId}/members
POST /loyalty/v1/{space}/games/{lookupId}/plays
etc

The Request Parameters

The following standard request parameters are available when saving:

Request parametersDescription
dim, dim_*The dimensions to be validated against the Entity's Dimension Filter and saved together with the payload. The Dimensions can either be specified as one concatenated string in a single dim parameter, or broken up into separate parameters prefixed by dim_
includeUnpublishedBy default, only ACTIVE (Published) Entities are considered. By setting this parameter to true, unpublished Entities are also accessible. This requires special permissions.
authLevelThis parameter lets you specify to what extent a Member has been recognized/authenticated. 0 means the Member is not confirmed (such as when entering your email address in a newsletter subscription form), 3 is a clickback from a targeted email, 5 is authenticated by password, 9 is highest level such as face to face contact and KYC. NOTE: This feature is not currently fully implemented and therefore not documented in the API specification.
responsetrue/false. If false (default), the save APIs will not return the saved content in the response. To receive the full response payload documented in the API specification, you have to pass in response=true. This is a performance feature, since it can take additional time and roundtrips to the database to prepare the full result, whereas, more often than not the response is actually not needed by the calling application. Only set to true when the response is needed.

The Payload

Since the Entity Id is specified in the URL it doesn't have to be specified in the payload. The Entity Lookup Id in the url always takes precedence and will be resolved to the internal Entity Id and then overwrite the entity Id attribute in the payload.

The Member Id in the payload always accepts a Member Lookup Id. This means you don't need to first lookup the internal member Id before saving. You can pass in for example the email address or mobile phone number in the Member Id field. It will be converted to the internal Member Id before the payload is saved.

There are certain content attributes which are read only and therefore are returned when you query the Entity but they are not documented or included in the payload used when saving. Examples of such attributes are MemberInfo, created, updated etc. They are safe to pass back to the save operation and will simply be ignored. This allows you to query content, make changes and then save it back as is. However, you will get an error if including any other attributes which are not part of either the query or the save documentation.

The NEON data model is comprehensive and contains most core fields. But every use case and client is different and there is often a need to save additional information for which there isn't a specific attribute in the model. NEON supports this through the data attribute. This is a freetext JSON attribute which can be added to and saved together with most content. NEON will verify that any data passed in the data attribute is valid JSON, but will otherwise not validate it.

Note, Dimensions in the payload are always ignored. Dimensions are taken from the dim_ query parameter when the content is created.

Dimensions

The dim parameter should always be passed in when saving, and it should contain all Dimensions that are relevant for the activity and transaction. The Dimensions get saved together with the content and enables detailed reporting and analysis.

The dim parameter will be verified against the Entity's Dimension Filter to ensure only content relevant to the Entity can be saved. There is no bypassDimensionFilter parameter when saving.

Note that the Dimensions are taken from the dim parameter (not the payload) and are saved when the content is first created. It doesn't get updated if you modify and save the content again. If you for example save a new Order on a German eCommerce site (dim=country:DE) and then fulfil and update the Order in the US (passing in dim=country:US when updating), the original dimensions will remain untouched. Likewise if you acquire a new Member using a particular promotion, and the Member later engages in a different promotion, this doesn't change the Member's origin.

🚧

Dimensions can't be changed

Make sure you pass in the correct Dimensions when saving new content because you can't change it by updating the content. If a mistake was made and you need Dimensions to be updated you have to contact SPIKA's support team

The POST response

A successful POST always return http status 200.

As mentioned above, the response payload depends on the response query parameter.

If the response query parameter is omitted or set to false, the response is empty:

{}

Or, in the case of HTTP status 201, where a row was created, the response payload may contain a single attribute containing the generated id. If you for example save a new Member the response will be 201 with the body:

{
  "isNew": true,
  "generatedId": "qwerty123"
}

If the response query parameter is true, a LoyaltyResponsewill be returned, which has the following format:

{
  "isNew": true, // Only present if the saved record was new
  createdId": "15437a9", // Only present if the saved record has a newly generated id
  "result": {
    // This is the fully populated response resource, usually a newly populated version of the payload that was passed in 
  },
  "unlockedOffers": {
    // Information about any offers that were unlocked by triggers as a result of the save
  },
  "receivedGifts": {
    // Any gifts that were given back as a result of the save
  },
  "competitionEntries": {
  	// Any competition that were entered into as a result of the save
	}
  // Other future automatic benefits may be added
}

The populated response payload sits in the result tag. The other tags depend on if there are any triggers associated with the Entity, and will only be populated if there is any additional data to return.

The purpose of including certain trigger outputs (such as competition entries etc) is for websites who would like to display the benefits to the Member.

Note, many Entities don't support triggers, but the return format is the same for consistency, and to cater for potential future functionality.

Bulk saves

Most API endpoints also support a bulk save option. Bulk saves are very useful when importing large amounts of data, for example from an external system or promotion.

Bulk saves operate take the same query parameters and operate the same way as standard single-record save operations (above), with the following things worth noting:

  • Bulk saves take a JSON List as payload, containing one or more objects. Each object is exactly the same as the individual payloads if the saves had been done through individual API calls.
  • The bulk saves accept up to 100 records per call.
  • All records are saved within the same transaction, meaning that if one of the rows fails then the whole bulk operation is rolled back.
  • Bulk saves are optimized for performance. It's not guaranteed that the individual objects are saved in the listed order. Triggers get executed at the end, once all records have been saved.
  • All rows in the bulk save must use the same Dimensions and belong to the same Entity. This is because they are specified as part of the url, and it lets NEON validate them once only.
  • All rows must relate to different content or identifiers. It's for example not possible to send multiple identical messages to the same person.
  • Bulk saves always return an empty response payload along with HTTP status 200.

NEON does not offer alternative asynchronous background batch processing of imports. If this is desired it can be implemented separately, utilizing the NEON bulk save functionality.

Actions

Some Entities support different actions to be applied. Examples of actions are:

  • Pick a code from a Codeset.
  • Redeem an Offer.
  • Send a Message.

Actions always operate on an Entity and sometimes on a saved record belonging to the Entity.

All actions are individual and there is therefore no general rule for if they:

  • take additional query parameters or not
  • take a payload or not
  • return a payload or not
  • support batch operations or not

However all actions do take the same dim and includeUnpublished attributes which gets validated in the same way as for save operations.

Here are some examples of action urls

// Pick a code. Does not take a request Payload, but returns a response payload with the picked code
POST .../codesets/{lookupId}/actions/pick?dim=...&member={lookupId} // Can't use as bulk operation

// Redeem a code
POST .../codeset/{lookupId}/codes/{code}/actions/redeem?member={lookupId} // Takes query parameters, no request payload. Returns a Code response

// Send one or multiple messages
POST .../messages/{lookupId}/actions/send // Sends one message, as per a special MessageSend payload
POST .../messages/{lookupId}/actions/send/bulk // Sends multiple messages to several Members, as per a list of MessageSend payloads