# SKUWEFT Supplier Preparation API

Base URL: https://skuweft.pages.dev/api/v1/supplier
OpenAPI: https://skuweft.pages.dev/supplier-openapi.json
Public capabilities: https://skuweft.pages.dev/agent-capabilities.json

Prepare supplier CSV/TSV files, buy credits through Stripe checkout and download
draft Shopify product CSVs without a SKUWEFT browser login. Files are processed
in server memory, not stored, logged by the application or sent to an AI provider.
The free browser checker and basic formatting fixes remain free.

## 1. Create an API account without a browser

Generate a management key locally using a cryptographically secure random source:
prefix lma_ followed by 32 random bytes encoded as 64 lowercase hex digits.
For Node.js: 'lma_' + randomBytes(32).toString('hex') from node:crypto.
Save it in a secret manager BEFORE registration. This is the only recovery method:
there is no email recovery. Never put keys in URLs, public code, logs or chat.

POST /accounts
Authorization: Bearer YOUR_MANAGEMENT_KEY
Content-Type: application/json

{"terms_accepted":true}

Read https://skuweft.pages.dev/terms before accepting. The response contains an
account_id. Retrying with the SAME management key returns the same account.
Do not generate a new account key just because a response was lost.
No trial/export credit is granted. This account is separate from browser passkey
accounts and listing-writing accounts: their balances do not transfer automatically.

## 2. Issue a limited operational key

Generate and privately save a second 32-byte random key, this time prefixed lms_.
POST /keys using the management key in Authorization:

{
  "api_key":"YOUR_NEW_OPERATIONAL_KEY",
  "scopes":["supplier:read","supplier:preview","supplier:export","supplier:checkout"],
  "export_limit":10,
  "expires_in_days":30
}

Read scope is mandatory. Grant only the other permissions the agent needs.
export_limit (0–1000) limits new paid file unlocks, not checkout payments.
Expiry is 1–90 days. The same key and settings are safe to retry and do not extend
expiry. Ten keys can be active and 100 can be issued over the account lifetime.
Only key hashes, prefixes, scopes, expiry and usage are retained. Secrets are never
returned by key listings. Keep the management key away from operational agents.

Use the operational key for all file, balance and checkout calls below.
GET /account reports credits and the key's remaining export allowance.

## 3. Preview a supplier file free

POST /preview with Content-Type: application/json and Authorization: Bearer YOUR_OPERATIONAL_KEY.

{
  "file":{
    "text":"Product name,Retail price,SKU\nCotton cushion cover,19.00,00123",
    "delimiter":",",
    "decimal":"dot",
    "mapping":{"title":0,"price":1,"sku":2}
  },
  "consent":true
}

Only process data the user authorized you to send to SKUWEFT. Source limits:
500 KiB, 1,000 rows, 100 columns. UTF-8 CSV, TSV or semicolon-delimited text only.
Save Excel as CSV UTF-8 first. JSON body limit is 3,200,000 bytes including escaping.
Mapping indexes start at zero. Title and retail price are required. Optional fields:
sku, description, vendor, type, tags, barcode, size, color, image and group.
Group is an explicitly chosen parent ID; no grouping or product facts are inferred.
Prices must already be in the store's currency. No currency conversion.

The response contains up to five output rows, counts, warnings, errors, ignored
columns, owned and a fingerprint of the normalized file/settings. Review all
findings and keep the fingerprint. Errors block export. There is no stored file ID;
you must keep the source and send it again for every export or re-download.

## 4. Buy one $9 USD credit

GET /plans first to check current price, availability and test_mode.
POST /checkouts with the operational key and an Idempotency-Key UUID header:

{"expected_amount_cents":900,"currency":"usd","credits":1}

Use one UUID per intended purchase and persist it for retries. The response gives
id, checkout_url, status, payment_count and credits_granted. Opening or creating
checkout does NOT charge anything. Your user or an authorized payment agent must
complete payment on the returned buy.stripe.com URL. No SKUWEFT browser login is
needed. SKUWEFT does not accept raw card details, off-session charge requests or
agent payment tokens. Never send either SKUWEFT key to Stripe.

Poll GET /checkouts/{id} no faster than every five seconds. Credits appear only
after the existing signature-verified Stripe webhook confirms a paid session.
Redirects, screenshots, receipts and client claims do not authorize downloads.
Payment Links are reusable: pay only once per intended purchase. After an ambiguous
payment result, poll first; do not create another payment blindly. After a payment
or reversal, the API returns checkout_url:null. Separate completed sessions on one
reused link each represent a real charge and are reported in payment_count.

## 5. Export and retry safely

POST /export with the same file object and:

{"file":{"text":"...","delimiter":",","decimal":"dot","mapping":{"title":0,"price":1}},"consent":true,"reviewed":true,"expected_fingerprint":"FINGERPRINT_FROM_PREVIEW"}

The response is text/csv, marked private/no-store. A successful first export uses
one credit and one unit of the operational key's export allowance. Exact retries
and re-downloads do not use another credit or allowance. A different source,
mapping or settings needs a fresh preview and another credit. Concurrent claims
are serialized in the database. One account cannot claim another account's files
or credits, even with the same fingerprint. Test and live credits stay separate.

A partial/full refund removes the associated credit and revokes its download
entitlement. With no other credit, downloads are blocked. Another unused credit
can unlock the file again, subject to key allowance; refunds do not reset allowance.

Products are draft and unpublished. Import as NEW products with overwriting off.
Review Shopify's preview; SKUWEFT does not connect to your store, publish products,
check existing handles, fetch images or guarantee imports. Inventory, shipping,
taxes, metafields, bundles and existing-product updates are unsupported.

## Key recovery, rotation and revocation

- GET /keys with the management key lists operational key IDs and usage.
- DELETE /keys/{id} with the management key revokes that key. In-flight responses may finish.
- POST /accounts/rotate with the current management key and
  {"new_account_key":"YOUR_FRESH_MANAGEMENT_KEY"} rotates account access and revokes
  ALL operational keys. Save the fresh key first. Credits remain on the account.
  If the response is lost, try GET /keys using the new key; if that fails, check
  the old key before retrying. Do not create a new account to recover the old one.
- Keep the management secret backed up privately. Losing it and all working
  operational keys means the account cannot be recovered through a browser/email.

## Limits and errors

Each key: 60 requests/minute. Each account: 60 preparation requests/hour and 20 new
checkouts/hour. Key administration: 30 requests/minute. Shared protection: 180
authenticated/registration requests/minute, 5 registrations/minute, 50/day and
5,000 total API accounts. Limits can temporarily prevent registration or requests
on the free service. Retry-After tells you when to retry a 429 response.

400 invalid_request/invalid_file: fix input. 401 invalid_api_key: verify key type,
expiry or revocation. 403 insufficient_scope/export_limit: ask the account
controller to authorize a suitable key. 402 payment_required: no usable supplier
credit. 404 not_found: wrong account, mode or ID. 409 preview_changed: preview the
exact new inputs; price_mismatch/key_conflict: resolve the conflict before retrying.
413 body_too_large. 415 json_required. 429 rate_limit. 503 unavailable: retry later.

Account/file responses are private/no-store. The application stores only hashed
credentials, preparation fingerprints, usage counters and required payment records,
not CSV contents, filenames or preview results. Hosting providers may retain normal
technical access metadata. API checkout attribution is unattributed; it does not
silently enroll agents in browser analytics. No AI provider call is made.
