Skip to main content

Overview

A bulk operation is a long running export or import that your app asks Salesive to do in the background. The API creates a row, a worker does the work in pages of 200 records, and your app polls the row for progress. Use it instead of paging through the ordinary list endpoints when a store has thousands of records. There are two kinds:

Authentication

Bulk operations are for installed apps only. Send your app_ access token:
The installation, and therefore the store, comes from the token. A merchant session is refused with 403, because a merchant has no installation to scope the work to. You never send a shop id.

Endpoints

All paths are under https://api.salesive.com/api/v1. The create, get, download and cancel routes check the scope the operation needs. The list route needs no extra scope: it only returns operations your own installation created.

Scopes

The required scope depends on the kind and the resource, not on the route alone. An import cannot target orders or customers; the create is refused with 400. A missing scope is 403 with a message naming the permission.

Caps

  • One active export and one active import per installation. A second create of the same kind is 409 while one is queued or running. A finished, failed or cancelled operation drops out of the active set.
  • Five operations per rolling hour per installation. The sixth create in any hour is 429, whatever the earlier operations’ statuses were.
  • The list returns the newest first. limit defaults to 20 and is capped at 50. The response is { "operations": [...] }, with each row in the operation shape below but without report.
The two active slots and the hourly count are enforced when the operation is created. A partial unique index on the installation and kind closes the race between two simultaneous creates, so the API cannot start two exports for one installation even under parallel requests.

Create an export

kind and resource are required. filters is optional and is whitelisted per resource. An unknown filter key is refused with 400 rather than ignored, so an export never returns more than the app asked for. The create answers 201 with the operation in data. An export also refuses sourceUrl. That field belongs to an import. The worker reads the store’s records in pages of 200 and writes one JSON object per line. An export is limited to 20,000 rows and 10 MB of JSONL. An export over either cap fails with a message asking for a narrower filter.

Download an export

When the export completes, the worker packages the JSONL as a single entry named export.jsonl inside bulk-export-<id>.zip and stores the zip in the shop’s private storage. The download route mints a URL to that package:
The zip contains exactly one file, export.jsonl. It is JSONL, not CSV: each line is one complete JSON object, with no header row and no enclosing array. The download URL is a credential and expires after 5 minutes. Do not log it or send it to another service.
The route is 409 when the operation is not a completed export, or when the export has no file. downloadAvailable on the operation is true only for a completed export with a stored file.

Create an import

The API fetches sourceUrl once, at creation, through the same SSRF guard as every other remote file. Every redirect hop is resolved and checked before it is requested, redirects are followed manually (at most 3), credentials in the URL are refused, and the body is abandoned as soon as it passes the cap. The fetched bytes are stored and the worker never fetches the URL again. An import does not take filters, and sourceUrl is required. The file must be JSONL: one JSON object per line. Import is create-only. A row that contains id or _id is refused, so an import can never overwrite an existing record. Unknown fields are refused too, so a typo does not silently create a record without the field you meant. name is required on every row. A product also needs a description and a category. A product, food or service needs a price. Every row is validated before it is written. A bad row is reported and skipped; the rows around it still import. A digital product cannot be imported with listed: true, because a listed title needs its file uploaded first.

Status and report

An operation moves through these statuses: The operation shape:
  • total is the rows expected: the row count for an export, the data line count for an import.
  • processed is rows written for an export, or rows applied successfully for an import.
  • report holds up to 100 per row import errors, each with the line number starting at 1 and a short message. It is returned by the single get only; the list leaves it out to keep the list small.
  • An import with rows but none applied ends failed. An import with some rows applied ends completed and carries the failure count in error.

Cancel

Only a queued or running operation can be cancelled. Anything else is 409. A cancel that lands while the worker is between pages, or during the export upload, wins: the operation ends cancelled and does not become completed.

Finished webhook

When an operation reaches completed, failed or cancelled, Salesive sends a bulk_operations/finished webhook to the installation that created it. It is a notify only event: the body carries the operation id, kind, resource, status, total, processed and error, never the file or its rows. The event follows your app’s webhookTopics subscription like any other topic. An app with no subscription list receives every topic.

Errors

Next steps

Rate limits

The per installation bucket that ordinary API calls share.

Webhooks

Verify the signature on bulk_operations/finished and the other topics.

List products

The synchronous list endpoint, for small reads.

App storage

Keep your app’s own state instead of writing it to store records.