Features Pricing Services
Compare
Resources
Contact Start Free Trial
Home/Docs/API & Webhooks

API & Webhooks

Soryk's API is the same one our admin and agent UIs use — there's no separate "public API" to maintain. This page lists what's available and how to call it. For deep integrations or custom workflows, the recommended pattern is: read directly from Shopify metaobjects (your data), use Soryk endpoints only for actions that need server logic (create payment, fix attribution, etc.).

i

API stability. The endpoints below are considered stable and follow semver conventions for breaking changes. We won't deprecate without notice.

Authentication

Two authentication modes:

Admin endpoints — Shopify session token

Embedded admin requests use the Shopify App Bridge session token. From inside the embedded app you don't need to do anything — App Bridge attaches the token automatically. From outside (rare):

curl -X GET https://your-shop.myshopify.com/apps/soryk/api/admin/agents \
  -H "Authorization: Bearer <session-token>"

Agent + buyer endpoints — JWT cookie

After magic-link or Google SSO sign-in, the JWT is in a HttpOnly secure cookie named soryk_session. Subsequent fetches include it automatically. Don't try to extract or forge it — sessions only work from the actual signed-in browser.

Available endpoints

Admin

MethodPathPurpose
GET / POST/api/admin/agentsList or create agents.
GET / PATCH / DELETE/api/admin/agents/[id]Read, update, delete an agent.
POST/api/admin/agents/bulk-importCSV bulk import.
POST/api/admin/agents/magic-linkSend magic-link invite.
GET / POST/api/admin/companiesList companies, create draft company.
GET / PATCH / DELETE/api/admin/commissions/rules/[id]Manage commission rules.
GET/api/admin/commissions/earningsComputed earnings (paid + pending).
GET / POST/api/admin/commissions/paymentsList or record payouts. Idempotent on (agentId, orderIds).
GET / POST/api/admin/commissions/overridesList or create attribution overrides.
GET/api/admin/commissions/exportCSV export. ?dataset=rules|due|paid.
GET / PATCH/api/admin/shop-configRead or update settings.
GET/api/admin/audit-logCombined metaobject + Redis mirror audit log.
POST/api/admin/territory-auditTrigger Claude conflict analysis.

Agent

MethodPathPurpose
POST/api/agent/request-loginSend OTP to agent email. Rate-limited.
POST/api/agent/verify-loginVerify OTP, set JWT cookie.
GET / PATCH/api/agent/profileRead or update locale.
GET/api/agent/companiesList assigned companies (territory-filtered if enabled).
GET/api/agent/catalogBrowse catalog with filters.
POST/api/agent/cart-previewCompute tax + shipping preview.
POST/api/agent/draft-orderCreate draft order with sanitized tags.
POST / PATCH / DELETE/api/agent/quotes/[id]Quote CRUD; PATCH on draft AND pending.
POST/api/agent/quotes/[id]/sendSend quote email.
POST/api/agent/quotes/[id]/convertConvert accepted quote to draft order.
GET/api/agent/payment-termsList Shopify PaymentTermsTemplate + label overrides.
GET / POST / DELETE/api/agent/push/subscribePush subscription management.

Buyer (when buyerPortalEnabled)

MethodPathPurpose
POST/api/buyer/[shop]/request-loginSend OTP to buyer email.
GET/api/buyer/catalogCatalog scoped to session companyId. Verifies location ownership.
GET/api/buyer/ordersOrders for the buyer's company.
POST/api/buyer/draft-orderPlace order. Tagged soryk_buyer:<email>.
GET/api/buyer/shipmentsFulfillment events.
GET/api/buyer/analyticsConsumption metrics for the buyer's company.

Public

GET / POST/quotes/[token] + /api/public/quotes/[token]/accept|rejectToken-based public quote flow. No auth.

Webhooks

Soryk subscribes to a small set of Shopify webhooks at install time. You don't need to configure anything.

TopicWhat we do
refunds/createCheck if any of the refunded order's lines were already in a paid commission. If yes, create soryk_commission_alert. Bust the analytics cache for the shop.
orders/paidUsed in some flows to trigger downstream computation (commission attribution refresh).
app/uninstalledMark the shop as uninstalled internally; queue Upstash purge.
customers/data_requestGDPR data subject access — return what we hold (transient, almost nothing).
customers/redact / shop/redactGDPR redaction handlers — delete any traceable PII from Upstash.

Rate limits

BucketLimit
Login (per email)5 / 5 min
Login (per IP)30 / 5 min
Admin payments60 / 1 min / shop
Other endpointsInherited from Shopify Admin API limits (40 RPS REST, ~50 cost/s GraphQL).

Excess returns 429 with Retry-After.

Examples

Curl — list agents

curl https://your-shop.myshopify.com/apps/soryk/api/admin/agents \
  -H "Authorization: Bearer <session-token>"

Curl — create a commission rule

curl -X POST https://your-shop.myshopify.com/apps/soryk/api/admin/commissions/rules \
  -H "Authorization: Bearer <session-token>" \
  -H "Content-Type: application/json" \
  -d '{
    "label": "Premium wines bonus",
    "scope": "collection",
    "scopeTarget": "gid://shopify/Collection/1234567890",
    "base": "no_tax_no_shipping",
    "ratePercent": 8,
    "priority": 10,
    "deductOnRefund": true
  }'

JavaScript — record a payment

const res = await fetch('/api/admin/commissions/payments', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    agentId: 'gid://shopify/Metaobject/...',
    orderIds: ['gid://shopify/Order/1', 'gid://shopify/Order/2'],
    amount: 250.00,
    currency: 'EUR',
    method: 'bank_transfer',
    note: 'April payout',
    paidAt: '2026-04-25'
  })
});

if (res.status === 409) {
  const { duplicateOrderIds, existingPaymentId } = await res.json();
  console.warn('Already paid', duplicateOrderIds, 'in payment', existingPaymentId);
}

Error codes

All error responses follow the same shape:

{
  "code": "INVALID_INPUT",
  "error": "Human-readable message"
}

Standard codes:

CodeHTTPMeaning
NOT_AUTHENTICATED401Missing or invalid session.
FORBIDDEN403Authenticated but not allowed (e.g. cross-tenant buyer request).
NOT_FOUND404Resource doesn't exist.
SHOP_NOT_CONNECTED400Shop reference present but no valid Shopify install.
INVALID_INPUT400Validation failed.
COMPANY_HAS_NO_CONTACT400Trying to send to a company without a primary contact email.
RATE_LIMITED429Too many requests; retry after the header.
DUPLICATE_PAYMENT409Bulk-pay rejected because some orders already in another payment.
INTERNAL500Server-side error. Check status page or contact support.

The agent UI translates codes via useErrorMessage() — keys live in lib/i18n/agent-dict.ts as error.<CODE> in EN/IT.

Was this helpful?
Edit this page on GitHub