Skip to main content

Event feed output files

An event feed picks up a file of activity from your SFTP server or S3 bucket, checks each row, and posts it as an event through the same rules as POST /events. Once every row has an outcome, the feed can write an output file back to the same server or bucket, listing what happened to each row: its status, the points it earned, the customer's balance afterwards, and why it failed.

You design the output in the Loom Loyalty Hub feed builder, on the feed's Output tab. Each environment sets its own output folder and file-name pattern.

Layout​

  • Format: CSV (comma-separated) or PSV (pipe-separated).
  • Columns: in the order you choose, each with an optional heading. A column heading row is optional.
  • Rows: every row of the input file, or only the rows that failed or conflicted. In failed-only mode, no file is written when nothing failed.
  • Header and trailer records: the output can echo the input file's own header and footer records, or define its own (see Header and trailer records).

The output is written once the whole file has been processed. Loom Loyalty keeps its own copy, which you can download from the feed's run in the Hub or through GET /event-feeds/{feedId}/runs/{runId}/reply-file.

Column types​

ColumnWhat it holds
Source columnA column of the input row, by name or position. Card numbers are redacted.
Line numberThe row's line number in the input file.
Row idThe row's transaction id.
StatusThe row's outcome (see Row statuses).
Points earnedThe points the row earned in one program currency, after caps.
Points totalThe points the row earned across all currencies.
Balance afterThe customer's available balance in one program currency right after the row posted.
Error codeThe row's error code from the list below. Blank on a row that posted.
Error descriptionThe error in plain words. Blank on a row that posted.
ReasonThe error code and description together, as CODE: description. Kept for existing feeds; use Error code and Error description for new ones.
FieldThe input field an error names. Kept for existing feeds.

How points and balances are counted​

  • Points count only what the row's own rules issued, after caps. Points issued by flows are not counted.
  • Balances are the customer's available (spendable) points. Pending points are not included.
  • A currency the customer has no account in reads 0.
  • A row that failed or was skipped earned 0 points. Its balance column shows the customer's current balance if the row got as far as posting, and is blank if it failed its checks first, before a customer was reached.
  • A DUPLICATE row (its transaction id was already posted) repeats the points it earned when it first posted, with the customer's balance as of now. Nothing is posted again. Resending a file you lost the output for is safe.
Points are issued immediately

When the output has any points or balance column, every row of the feed issues its points as it posts, whatever each rule's own timing setting says. This makes the numbers in the file final, and it makes large files take longer to process.

Formatting values​

Each column, and each header and trailer field, can take a list of steps that run in order on its value.

StepSettingsWhat it does
Trim—Removes leading and trailing spaces.
CaseUPPER or LOWERChanges letter case.
Format numberdecimal places, decimal separator, grouping separatorWrites a number with fixed decimal places, for example 1234.5 as 1,234.50.
Implied decimalsplacesMoves the decimal point right and drops it, for example 12.34 with 2 places becomes 1234.
Format datepatternWrites a date in a pattern, for example yyyyMMdd.
Padwidth, character, sidePads the value to a fixed width, for example 500 padded to 9 with 0 on the left becomes 000000500.
DefaultvalueUses a fixed value when the value is empty.
Lookuptable, defaultReplaces the value with its entry in one of the feed's lookup tables.
ExpressionCEL expressionSets the value with a sandboxed CEL expression.

An output expression can read the row's points and balances (maps from currency code to amount) and its status. balances only holds the currencies the output's balance columns name, so guard any other currency with has(balances.PTS).

Header and trailer records​

A header or trailer record is one line made of fields, each with its own formatting steps:

FieldWhat it holds
ConstantA fixed value, such as a record type of T.
Run date and timeWhen the output was written, in an optional date pattern.
Line countHow many rows the output lists, or only the rows with the chosen statuses.
Points totalThe points all the file's rows earned in one currency.

For example, a trailer of a T constant, a line count, and a points total padded to 9 digits writes T|3|000001150.

Row statuses​

StatusMeaning
POSTEDThe row posted as an event.
SKIPPEDThe feed's skip condition held, so the row was not posted. This is not a failure.
FAILEDThe row failed a check or could not post. See its error code.
DUPLICATEThe transaction id was already posted with the same content. It was not posted again.
CONFLICTThe transaction id was already posted with different content. It was not posted.

Error codes​

Match on the error code, not the description: codes are stable, and descriptions may be reworded.

Row errors​

CodeMeaning
CARD_NUMBER_DETECTEDA value holds a full card number. Card numbers are not accepted.
INVALID_DATEA date couldn't be read or written with its pattern.
INVALID_NUMBERA value couldn't be read as a number.
INVALID_VALUEA value doesn't fit the event field's type.
LOOKUP_NO_MATCHA lookup found no entry for the value, and has no default.
EXPRESSION_FAILEDAn expression raised an error.
EXPRESSION_LIMIT_EXCEEDEDAn expression ran too long.
SKIP_CONDITION_FAILEDThe skip condition couldn't be evaluated.
ROW_ID_MISSINGThe row's transaction id is empty.
ROW_ID_TOO_LONGThe row's transaction id is longer than 255 characters.
EVENT_ID_INVALIDThe feed reads the event id from the file, and this row's id is not a UUID.
CUSTOMER_KEY_MISSINGThe row's customer key is empty.
CUSTOMER_NOT_FOUNDNo customer has the row's customer key.
CUSTOMER_AMBIGUOUSMore than one customer has the row's customer key.
EVENT_DATE_TIME_INVALIDThe row's event date is missing or isn't a valid date.
EVENT_TOO_OLDThe row's event date is older than the feed's maximum age.
EVENT_IN_FUTUREThe row's event date is more than 24 hours after the file arrived.
REQUIRED_FIELD_MISSINGA required field is missing or blank.
TYPE_MISMATCHA value isn't of the type a check expects.
VALUE_NOT_ALLOWEDA value isn't one of the allowed values.
OUT_OF_RANGEA number is outside the allowed range.
LENGTH_OUT_OF_RANGEA value is too short or too long.
PATTERN_MISMATCHA value doesn't match the required pattern.
EXPRESSION_CHECK_FAILEDAn expression check didn't pass.
CONDITION_NOT_META condition check didn't hold.
EVENT_DATA_INVALIDThe mapped event doesn't match the event type's data model.
DUPLICATE_ROWThe transaction id was already posted, or appears earlier in the file, with the same content.
ROW_CONFLICTThe transaction id was already posted, or appears earlier in the file, with different content.
RULE_FAILEDThe row posted, but a rule rejected it. The same transaction id can be sent again in a later file.
PROCESSING_ERRORThe row posted, but its processing kept failing. Contact support with the run and line number.
NOT_POSTEDThe row passed its checks but was never posted, because the stored file changed during the run.
EVALUATION_FAILEDThe row couldn't be evaluated because of an unexpected error. Contact support with the run and line number.

File errors​

These fail the whole file. No row is posted, and the run reports the code instead of an output file.

CodeMeaning
COUNT_MISMATCHThe footer's record count differs from the file's rows.
CONTROL_TOTAL_MISMATCHThe footer's control total differs from the sum of the rows' values.
HEADER_RECORD_MISSINGThe feed expects a header record, and the file has none.
FOOTER_RECORD_MISSINGThe feed expects a footer record, and the file has none.
FOOTER_RECORD_INVALIDThe footer's record count or control total isn't a number.
INVALID_ENCODINGThe file isn't valid in its encoding.
UNTERMINATED_QUOTEA quoted value is never closed.
LIMIT_EXCEEDEDA row is too long, the file has too many rows, or processing took too long.

Run-completed webhook​

When a run finishes, the event_feed_run.completed webhook carries its line counts and, as an optional pointsEarned field, the points the run's rows earned per currency. See Webhooks.