Bulk changes with CSV imports

Change thousands of records at once with the same CSV imports and exports as Store Admin: upload, review the preview, confirm, follow the job.

Large changes travel as CSV files, never inside a request: upload the file, let Tringify analyze it exactly like the Store Admin import wizard, review what would change, then confirm. The reference for every endpoint is CSV imports and exports.

When to use CSV imports#

Use a CSV import when a change touches more records than one request should carry: a catalog of thousands of products, a price update across a range, a customer migration or a whole language of translations. The file travels through an upload URL, Tringify analyzes it exactly like the Store Admin import wizard, and nothing changes until you confirm. Exports work the other way: start one, then download the file from a short-lived link. Store Admin API tokens and connected AI tools can use these endpoints; app tokens cannot.

Import in five steps#

  1. Check the type: GET /imports/types lists every import, its modes (the first is the default), its options and the scopes it needs.

  2. Upload the file: POST /imports/upload-intents with the type (and resource for translations) and one item describing the file. PUT the file's bytes to the returned upload_url, then call POST /imports/upload-intents/finalize and poll POST /imports/upload-intents/status until the intent is ready; both take the type and the intent_ids only (the intent keeps the resource given when it was created). The type's Store Admin limits on size, rows and columns apply here.

  3. Create the import: POST /imports with the ready file_id, the mode and any options. The response is the import in preview, with status ready or blocked.

  4. Review the preview: show the counts (creates, updates, unchanged, skips, errors), the warnings (for example ignored columns) and the row errors from GET /imports/{importId}/errors before going on.

  5. Confirm: POST /imports/{importId}/confirm starts the job. Poll GET /imports/{importId} until status is completed, failed or cancelled; job carries the progress and the result counts, and the errors endpoint now returns the rows the job could not apply.

http
POST /api/2025-01/imports
Idempotency-Key: catalog-update-2026-10-27

{
  "type": "products",
  "file_id": "5d3a1c7e-2f4b-4a6c-8e9d-0b1c2d3e4f5a",
  "mode": "update_only",
  "options": { "create_redirects": true }
}

201 Created
{
  "success": true,
  "data": {
    "import": {
      "id": "3f0c9a1e-6b2d-4e8f-9a7c-1d2e3f4a5b6c",
      "type": "products",
      "mode": "update_only",
      "status": "ready",
      "preview": { "rows": 1200, "creates": 0, "updates": 1180, "unchanged": 14, "skips": 0, "errors": 6, "can_confirm": true },
      "job": null,
      "expires_at": "2026-10-28T14:02:00-04:00"
    }
  }
}

A preview stays valid for 24 hours. Analyzing a large file can take up to 25 seconds; when the service is busy the request returns 503 and can be repeated with the same Idempotency-Key. Send an Idempotency-Key on every create so a retry after a timeout returns the same import instead of a second one.

Rows with errors#

Most types need an error-free file: when the preview has errors, can_confirm is false and status is blocked. Fix the rows the errors endpoint lists, upload the file again and create a new import. Imports marked partial (reviews, historical orders and translations) apply the valid rows and skip the rest, as the Store Admin wizard does. Each error names the CSV row, the field and, when it maps to one, the column header as your file shows it, plus a code, a message and, when the error has them, details (for example existing_id, the resource that already holds a duplicate value).

A problem with the whole file is listed the same way, on row 1 (the header row), with field naming the column: a missing required column (IMPORT_EXPORT_CSV_MISSING_REQUIRED_COLUMN, one error per column), a duplicate, blank or unsupported column, a file over the type's size, row or column limits, or a plan limit the file would exceed. Update files need the ID column of an export, and for categories and attributes also its Revision column.

An unchanged export imports again without errors in the type's update mode. Attributes export one row per option with the attribute's fields on every row; that file imports with update_attribute_options, or with update_attributes to change attribute fields only, where option cells that still match the store are ignored and an edited option cell is a row error. An attribute takes its fields from its first row: a later row whose field differs from both the first row and the stored value is ATTRIBUTE_IMPORT_FIELD_CONFLICT. Stock level rows need a Reason only where Quantity changes, and redirects the store already has are skipped. Reviews, tags and historical orders only add records, so their export is a file to move data to another store, not to edit in place.

A translation import's preview runs exactly the checks the job runs, with the same uniqueness rule as the translation API: per language, category names are unique among categories with the same parent, slugs (and vendor, brand, blog category and blog tag names) among the values shown in that language, and tag and attribute names among translated names. Two rows of one file that would share a value: the earlier row keeps it and the later row is an error. A row the preview accepts fails in the job only if the store changed in between, and then with its real code, never a generic one. The job applies the file in batches of at most 500 rows, each checked in one pass and saved in its own short transaction, so a running import does not hold up other translation writes: a write of another resource type, another language or another value never waits for it, and a write that touches an item or a name or slug the current batch is writing waits at most 2 seconds and otherwise fails with TRANSLATION_WRITE_BUSY (nothing saved; send it again after error.details.retry_after_seconds).

Gift Card CSV files#

For Gift Cards, use type gift_cards. Import issues a new card for each row and requires Currency, Amount and Reason; it cannot update existing cards. A preview with any error cannot be confirmed. Delivery defaults to manual (nothing is sent); sending choices come from Gift Card settings and require the matching recipient email or phone. Send at and Expires at must be future RFC 3339 timestamps with an offset; a scheduled delivery must be sent, and expiry must be after delivery. Card amounts and text use the limits returned by Get Gift Card limits. Codes are generated by Tringify and never appear in CSV files or row errors. Retrying or resuming one import never issues its rows twice; uploading the file as a new import issues new cards. Imports can be cancelled, but cards already issued remain and there is no undo. Export lists cards newest first with only Code (last 4), balances, recipient details, status, source, language and RFC 3339 dates in the store timezone.

Use write_gift_cards to upload and confirm an import, and read_gift_cards to read its progress or export cards. These CSV endpoints support Store Admin tokens and connected tools. The Gift Card preview checks every row; failures after confirmation appear in the job's row errors while other rows continue. Job error counts include every failed row; row_error_limit and the failed-row count show when only the first errors are retained. Cancelling stops later batches; a batch already issuing may finish. Every new card has the same issuance history, audit entry, delivery and gift_cards/create webhook as Issue a Gift Card. No code is included in a CSV result or webhook.

Import column

Field

Required

Currency

currency_code

Required

Amount

amount

Required

Reason

reason

Required

Recipient name

recipient_name

Optional

Recipient email

recipient_email

Optional

Recipient phone

recipient_phone

Optional

Message

recipient_message

Optional

Language

locale

Optional

Delivery

delivery_method

Optional

Send at

scheduled_at

Optional

Expires at

expires_at

Optional

Currency must be the store's base currency or an enabled currency. Amount must be positive and within issue_max and the currency's precision; Reason is required and stays in merchant history. Recipient name and Message follow the text limits in Get Gift Card limits. Recipient email must be valid; Recipient phone needs its country calling code. Language must be offered by the store; blank uses its default. Delivery can be manual, email, phone or both, when offered in delivery_options. Sending needs the matching recipient details. Blank Send at sends immediately when a sending method is chosen; a future Send at keeps the card scheduled until then. Blank Expires at adds no expiry. Import headers are matched case-insensitively. An export is a record of existing cards and is not an update template.

Export columns, in order: ID, Code (last 4), Currency, Initial value, Balance, Status, Source, Order ID, Recipient name, Recipient email, Recipient phone, Message, Language, Issued at, Activated at, Expires at, Send at, Updated at. Export is a background operation over pages; values reflect the cards when each page is read. Files use UTF-8 with a BOM and escape spreadsheet formulas, as other CSV exports do.

Gift Card row error

Message (parameters filled by the server)

GIFT_CARD_IMPORT_REQUIRED

{{.column}} is required.

GIFT_CARD_IMPORT_AMOUNT_INVALID

{{.column}} must be more than 0 and at most {{.max}}, with at most {{.decimals}} decimal places.

GIFT_CARD_IMPORT_TOO_LONG

{{.column}} can have at most {{.max}} characters.

GIFT_CARD_IMPORT_EMAIL_INVALID

{{.column}} must be a valid email address.

GIFT_CARD_IMPORT_PHONE_INVALID

{{.column}} must be a phone number with its country calling code, like +15551234567.

GIFT_CARD_IMPORT_DATE_INVALID

{{.column}} must be a future date and time, like 2026-12-24T09:00:00+01:00.

GIFT_CARD_IMPORT_EXPIRY_BEFORE_DELIVERY

{{.column}} must be after Send at.

GIFT_CARD_IMPORT_DELIVERY_UNAVAILABLE

{{.column}} must be one of: {{.options}}.

GIFT_CARD_IMPORT_RECIPIENT_REQUIRED

{{.column}} is required for this delivery.

GIFT_CARD_CURRENCY_NOT_ENABLED

Issue Gift Cards in the store's base currency or another enabled store currency. {{.currency}} is not enabled.

GIFT_CARD_LANGUAGE_UNAVAILABLE

Choose one of your store's languages.

GIFT_CARD_IMPORT_UNSUPPORTED_COLUMN

{{.column}} isn't a Gift Card import column. Remove it or use the import template.

Import types#

Type

Records

Modes

Options

Scopes to run

Supports

products

Products

create_only, update_only

create_redirects

write_products, write_files

cancel

product_sales_baselines

Product sales baselines

—

—

write_products

cancel

categories

Categories

create_only, update_only

create_redirects

write_products

cancel

attributes

Attributes

create_only, update_attributes, update_attribute_options

—

write_products, write_files

cancel

tags

Product tags

—

—

write_products

cancel

vendors

Vendors

create_only, update_only

create_redirects

write_products

cancel

brands

Brands

create_only, update_only

create_redirects

write_products

cancel

customers

Customers

create_only, update_only

—

write_customers

cancel

gift_cards

Gift Cards

—

—

write_gift_cards

cancel

reviews

Reviews

—

—

write_reviews

partial, cancel

orders

Historical orders

—

—

write_orders

partial, cancel, undo

stock_levels

Stock levels

—

—

write_inventory

cancel

inventory_groups

Inventory groups

—

—

write_inventory

cancel

locations

Locations

create_only, update_only

—

write_locations

cancel

menus

Menu items

create_only, update_only

—

write_menus

cancel

redirects

URL redirects

—

—

write_redirects

cancel

tax_rates

Tax rates

create_only, update_only

market_id (required)

write_markets

cancel

translations

Translations

—

resource (required), locale (required)

write_translations + the resource's owner write scope

partial

The first mode is the default. These modes and options are exactly what POST /imports accepts, from the same catalog the importer checks; any other mode is IMPORT_EXPORT_INVALID_MODE naming the type's modes.

Discard, cancel and undo#

POST /imports/{importId}/discard drops an import that was not confirmed. Reviews, tags, historical orders, sales baselines and tax rates keep one waiting preview per store, so discard an unwanted one before creating another (IMPORT_EXPORT_PREVIEW_ALREADY_ACTIVE). POST /imports/{importId}/cancel stops a running job; rows already applied stay applied. A finished historical order import can be undone with POST /imports/{importId}/undo: the first call returns CONFIRMATION_REQUIRED with how many orders will be deleted and how many changed since the import and are kept; repeat it with confirmed: true and the confirmation_token after the merchant agrees. One call deletes a bounded batch, so repeat the round while remaining is above 0.

Exports#

POST /exports with a type and its options starts the same export as the Export button in Store Admin. Poll GET /exports/{exportId} until status is completed; the response then carries download_url, a signed link valid for 5 minutes. Read the export again for a fresh link. Export files use the import columns, so an edited export can be imported again, except Gift Cards: their export is a listing (no Amount or Reason, only the last 4 code characters) and their import only issues new cards.

Type

Records

Options

Scopes

products

Products

status, vendor_id, brand_id, category_id

read_products

categories

Categories

—

read_products

attributes

Attributes

mode

read_products, read_files

tags

Product tags

—

read_products

vendors

Vendors

—

read_products

brands

Brands

—

read_products

customers

Customers

—

read_customers

subscribers

Newsletter subscribers

—

read_customers

gift_cards

Gift Cards

—

read_gift_cards

reviews

Reviews

—

read_reviews

orders

Orders

—

read_orders

stock_levels

Stock levels

location_id, item_type, low_stock_only, search

read_inventory

stock_history

Stock history

location_id, item_type, reason, search, from_date, to_date

read_inventory

inventory_groups

Inventory groups

—

read_inventory

locations

Locations

—

read_locations

menus

Menu items

—

read_menus

redirects

URL redirects

—

read_redirects

tax_rates

Tax rates

market_id (required)

read_markets

tax_rate_reference

Tax rate reference

market_id (required)

read_markets

tax_zones

Tax zones

market_id (required)

read_markets

tax_classes

Tax classes

market_id (required)

read_markets

translations

Translations

resource (required), locale (required)

read_translations + the resource's owner read scope

Translate a whole language#

  1. Export the source and current translations: POST /exports with type: "translations" and options: { "resource": "product", "locale": "fr" }, then download the file.

  2. Translate the file, keeping its id columns and headers.

  3. Upload it with type: "translations" and resource: "product", then POST /imports with the same resource and locale options.

  4. Review the preview (new translations count as creates, overwritten ones as updates) and confirm.

Translation resources: attribute, attribute_option, blog_author, blog_category, blog_category_metafield, blog_post, blog_post_metafield, blog_tag, brand, brand_metafield, category, category_metafield, collection, collection_metafield, menu_item, metafield_choice, metafield_definition, page, page_metafield, product, product_filter_definition, product_media, product_metafield, store_metafield, tag, variant, variant_metafield, vendor, vendor_metafield. The locale must be an enabled, non-default store language.

Translation CSV resources include message_template (one SMS or WhatsApp message type and channel; content), email_template (one email editor text per row; content), order_status (one custom status; name), file and product_media (alt_text). Email text_id is the template UUID followed by : and the exact text_key from the export: subject, preview_text, preheader, a reachable block text or a code-mode text run. Keep these identifiers, source columns and headers unchanged; edit only translated columns. Email translations change text, preserving layout and template syntax. Message and email translations must keep every {{ ... }} value and {% ... %} tag. Blank cells keep stored translations; __CLEAR__ clears one. System order statuses are supplied by Tringify and cannot be translated. The preview, row errors, file limits and job progress are the same as other translation CSVs.

Translation resource

CSV columns

message_template

template_id, template_name, template_type, channel, source_content, translated_content

email_template

text_id, template_name, template_id, text_key, text_kind, source_content, translated_content

order_status

status_id, status_name, status_slug, translated_name

file

file_id, file_alt_text, translated_alt_text

product_media

media_id, media_alt_text, translated_alt_text

For emails, the export supplies the exact editor text identifiers; no separate per-email Store API translation endpoint is offered. Send type translations with resource email_template through the ordinary CSV workflow. Email subjects, preview text, preheaders, reachable visual block texts and code-mode text runs retain their editor granularity. Standard AI Translator jobs are available in Store Admin and mobile, using the same estimates, credits, progress, review and apply flow as other translation resources. Theme text keeps its storefront/theme-scoped translation workflow and CSV format; it is not a resource option of these CSV endpoints.

Connected AI tools#

The hosted MCP server offers the same steps as tools: list_import_types, create_import_upload, finalize_import_upload, get_import_upload_status, create_import, list_import_errors, confirm_import, get_import, list_imports, discard_import, cancel_import, undo_import, list_export_types, create_export, get_export, list_exports and cancel_export. The tool uploads and downloads the file with its own HTTP client; file contents never go into tool arguments or replies. A tool is listed when the connection has any permission an import or export type needs, and each call needs that type's permissions.