Gift card mapping
StagingGiftcard is a flat shape — code, value, expiry, optional
recipient, and a debit history that’s passed through to Shopify
unchanged.
Gift cards depend on the customer migration. The staging model
declares requires_additional_mapping = customer so customer_id
/ recipient_id get rewritten from source IDs to Shopify GIDs at
load time. Run the customer migration first.
Quick reference — minimum loadable gift card
{
"original_id": gift_card_id,
"code": code,
"initialValue": face_value
}Identity & value
codestringThe redemption code on the card. Must be unique per shop.
"code": codeinitialValuenumberThe original face value at issue. Set once — Shopify computes
current balance as initialValue minus the sum of debits.
"initialValue": face_valueexpiresOnstring (ISO8601 date)Expiry date. Omit for cards that never expire.
"expiresOn": expires_atnotestringInternal admin note.
"note": "Migrated from " & source_platformCustomer & recipient
Source-platform IDs here. Graftport rewrites them to Shopify GIDs at load time.
customer_idstringSource-platform customer ID for the cardholder. Resolved to a Shopify customer GID using the customer migration’s results.
"customer_id": customer_idrecipient_idstringSource-platform customer ID for the gift recipient (when the card was purchased as a gift for someone else). Also resolved to a Shopify GID.
messagestringPersonal gift message.
preferredNamestringWhat the recipient wants to be called in the card-delivery email.
Debit history (pass-through)
debitsobject[]Array of past debit transactions. Each entry has Shopify’s
DebitTransaction shape:
| Field | Notes |
|---|---|
processedAt | ISO8601 of when the debit happened. |
note | Optional reason. |
debitAmount | { amount, currencyCode }. |
"debits": redemptions.{
"processedAt": redeemed_at,
"note": "Order " & $string(order_id),
"debitAmount": {
"amount": $string(amount),
"currencyCode": currency
}
}Patterns
Source has just code + balance (no debit history)
If the source only tracks current balance, issue at the current balance and skip debits — the customer-visible amount is preserved even though the history is lost:
{
"original_id": gift_card_id,
"code": code,
"initialValue": current_balance,
"expiresOn": expires_at,
"note": "Migrated from " & source_platform & " — balance preserved, history dropped"
}Source has full debit history
{
"original_id": gift_card_id,
"code": code,
"initialValue": original_amount,
"expiresOn": expires_at,
"customer_id": customer_id,
"debits": redemptions.{
"processedAt": redeemed_at,
"note": "Order " & $string(order_id),
"debitAmount": {
"amount": $string(amount),
"currencyCode": currency
}
}
}Gift purchased for someone else
{
"original_id": gift_card_id,
"code": code,
"initialValue": face_value,
"customer_id": purchaser_customer_id,
"recipient_id": recipient_customer_id,
"preferredName": recipient_name,
"message": gift_message
}Gotchas
Gift cards are real money. Always extract + dry-run transform first; spot-check a sample of staged records to confirm values and balances are right before loading.
initialValueis set once and can’t be changed. Decide upfront whether to issue at original face value (preserves history via debits) or current balance (drops history). Document the choice for the merchant.- Code uniqueness is enforced. Duplicates fail the load. Suffix in the mapping if your source allows duplicate codes.
customer_id/recipient_idare source IDs, not Shopify GIDs. Graftport rewrites them — but only if those customers were migrated. If the customer wasn’t migrated, the link drops to null.- Currency. Each Shopify shop has a single gift card currency. Multi-currency cards from the source need to be converted in the mapping.
- Expired cards. Setting
expiresOnin the past creates an already-expired card on Shopify. Usually fine — preserves the historical state — but customers can’t redeem. - Debits are pass-through validated. Each entry runs through
Shopify’s
DebitTransactionschema; malformed entries (missingdebitAmount, unparseableprocessedAt) fail the row.