Skip to content

AGENTS.md

144 lines · 4.6 KB · Markdown
Raw

@kody/shopify — agent notes

Human setup and intent live in README.md. This file is for agents: imports, smoke/dryRun execute snippets, and edge cases. Secrets by name only — never paste token values. Do not disable live webhooks or jobs.

Auth

LaneCredentialNotes
PreferredUser secrets shopifyClientId + shopifyClientSecretDev Dashboard client credentials; token exchanged per invoke
LegacyUser secret shopifyAdminAccessTokenStatic Admin token (shpat_…); wins if present

Pass shop: "your-store" (myshopify subdomain) on each call. Approve host {shop}.myshopify.com. Default API version 2026-07 (apiVersion override). No platform Shopify OAuth integration.

Import paths

ExportImport
package overviewkody:@kody/shopify
graphqlkody:@kody/shopify/graphql
requestkody:@kody/shopify/request
shopkody:@kody/shopify/shop
productskody:@kody/shopify/products
orderskody:@kody/shopify/orders
customerskody:@kody/shopify/customers
inventorykody:@kody/shopify/inventory
collectionskody:@kody/shopify/collections
fulfillmentskody:@kody/shopify/fulfillments
draft-orderskody:@kody/shopify/draft-orders
webhookskody:@kody/shopify/webhooks
metafieldskody:@kody/shopify/metafields
configkody:@kody/shopify/config
smoke-testkody:@kody/shopify/smoke-test
typeskody:@kody/shopify/types

Prefer static kody:@kody/shopify/... imports from execute. Do not lead with packages.invoke.

Default exports: ./graphql → graphql; ./request → shopifyRequest; ./shop → getShop; ./products → listProducts; ./orders → listOrders; ./customers → listCustomers; ./inventory → listLocations; ./collections → listCollections; ./fulfillments → listFulfillmentOrders; ./draft-orders → listDraftOrders; ./webhooks → listWebhooks; ./metafields → listMetafields; ./config → getConfig; ./smoke-test → smokeTest.

Smoke test

./smoke-test always runs local shop/id helper checks. When shop and credentials are present it also reads shop { name }.

import smokeTest from 'kody:@kody/shopify/smoke-test'

export default async function main() {
	return await smokeTest({ shop: 'acme' })
}

Common snippets

Read shop, products, unpaid orders:

import { getShop } from 'kody:@kody/shopify/shop'
import { listProducts } from 'kody:@kody/shopify/products'
import { listOrders } from 'kody:@kody/shopify/orders'

export default async function main() {
	const shop = await getShop({ shop: 'acme' })
	const products = await listProducts({
		shop: 'acme',
		query: 'status:active',
		first: 10,
	})
	const unpaid = await listOrders({
		shop: 'acme',
		query: 'financial_status:pending',
		first: 10,
	})
	return { shop, products, unpaid }
}

Unwrapped GraphQL:

import { shopifyGraphql } from 'kody:@kody/shopify/graphql'

export default async function main() {
	return await shopifyGraphql({
		shop: 'acme',
		query: `query ($query: String) {
			products(first: 5, query: $query) {
				nodes { id title handle }
			}
		}`,
		variables: { query: 'vendor:acme' },
	})
}

dryRun (writes)

Writes default to dry-run. Pass dryRun: false only after the user confirms the exact shop and mutation.

import { createProduct } from 'kody:@kody/shopify/products'

export default async function main() {
	const preview = await createProduct({
		shop: 'acme',
		title: 'Hiking backpack',
		status: 'DRAFT',
	})
	// => { dryRun: true, wouldCall: { mutation: 'productCreate', product: { ... } } }

	const created = await createProduct({
		shop: 'acme',
		title: 'Hiking backpack',
		status: 'DRAFT',
		dryRun: false,
	})
	return { preview, created }
}

Edge cases / fork notes

  • GraphQL is the supported Admin surface. REST remains as ./request for older resources still documented there.
  • Requests stay on {shop}.myshopify.com/admin/…. Arbitrary hosts are rejected.
  • Client-credentials tokens are requested per invoke and never written to packageStorage().
  • Rate limits: GraphQL uses calculated query cost; REST is 40 req/min (higher on Shopify Plus). The client retries 429 / 5xx and honors Retry-After.
  • Helpers project slim objects. Use ./graphql when you need extra fields.
  • Numeric ids are accepted and normalized to gid://shopify/{Type}/{id}.
  • Fork first: do not treat live @kody/shopify packageStorage() as yours.
  • Never paste Shopify client secrets or Admin access tokens into chat or logs.