admin-graphql
Build Shopify Admin GraphQL queries and mutations for products, orders, customers, inventory, and more. Covers cost-aware rate limiting, cursor pagination, bulk operations, global resource identifiers, and version-safe API usage.
Works with
---
name: admin-graphql
description: Build Shopify Admin GraphQL queries and mutations for products, orders, customers, inventory, and more. Covers cost-aware rate limiting, cursor pagination, bulk operations, global resource identifiers, and version-safe API usage.
license: MIT
---
## When to Use This Skill
Use **Admin GraphQL API** when you need to:
- Manage products, variants, collections, and pricing (preferred over REST for any business logic)
- Query or modify orders, fulfillments, and refunds
- Create or update customers and customer accounts
- Adjust inventory across multiple locations
- Create metafields and metaobjects for custom data
- Set up webhooks for event subscriptions
- Perform bulk operations on large datasets (1000+ records)
- Access the latest Shopify features (GraphQL-only endpoints)
**GraphQL vs REST comparison:**
| Feature | GraphQL Admin | REST (Deprecated) |
|---------|--------------|-------------------|
| Rate Limiting | Cost-based; standard restore rate is 100 points/sec | Request bucket; standard restore rate is 2 requests/sec |
| Pagination | Cursor-based (Relay pattern) | Offset/limit (deprecated) |
| Field Selection | Precise (fetch only needed fields) | Fixed response shape (wasteful) |
| Batch Operations | Native bulk operations (JSONL) | Multiple sequential requests |
| Latest Features | Current platform features | Some newer features are GraphQL-only |
| Status | Required for new public apps | Legacy since October 1, 2024 |
---
## API Endpoint & Authentication
**Endpoint:** `https://{shop}.myshopify.com/admin/api/2026-07/graphql.json`
**Authentication:**
```javascript
const headers = {
'Content-Type': 'application/json',
'X-Shopify-Access-Token': process.env.SHOPIFY_ACCESS_TOKEN,
};
```
---
## Cost-Based Rate Limiting
Shopify uses calculated query costs. The standard GraphQL Admin API restore rate is 100 points per second; Advanced, Plus, and enterprise plans have higher rates. A single query can't exceed 1,000 requested points. Read `extensions.cost.throttleStatus` instead of hard-coding one bucket size.
**Handling throttling:**
```javascript
async function executeWithRetry(query, variables, maxRetries = 3) {
for (let attempt = 1; attempt <= maxRetries; attempt++) {
const response = await fetch(endpoint, {
method: 'POST',
headers,
body: JSON.stringify({ query, variables }),
});
const data = await response.json();
if (data.errors?.some(e => e.extensions?.code === 'THROTTLED')) {
const waitTime = Math.pow(2, attempt - 1) * 1000;
console.log(`Throttled. Waiting ${waitTime}ms...`);
await new Promise(resolve => setTimeout(resolve, waitTime));
continue;
}
return data;
}
throw new Error('Max retries exceeded');
}
```
---
## Cursor-Based Pagination
```graphql
query GetProducts($first: Int, $after: String) {
products(first: $first, after: $after) {
pageInfo {
hasNextPage
endCursor
}
edges {
node {
id
title
}
}
}
}
```
**JavaScript iteration:**
```javascript
const allProducts = [];
let hasNextPage = true;
let endCursor = null;
while (hasNextPage) {
const data = await executeQuery(GET_PRODUCTS, {
first: 50,
after: endCursor,
});
const { edges, pageInfo } = data.products;
allProducts.push(...edges.map(e => e.node));
hasNextPage = pageInfo.hasNextPage;
endCursor = pageInfo.endCursor;
}
```
---
## 30 Essential Operations
### Product Management
**1. Create Product**
```graphql
mutation CreateProduct($input: ProductInput!) {
productCreate(input: $input) {
product {
id
handle
title
status
}
userErrors {
field
message
}
}
}
```
**2. Update Product**
```graphql
mutation UpdateProduct($input: ProductInput!) {
productUpdate(input: $input) {
product {
id
title
updatedAt
}
userErrors {
field
message
}
}
}
```
**3. Create Variants (Bulk)**
```graphql
mutation BulkCreateVariants($productId: ID!, $variants: [ProductVariantsBulkInput!]!) {
productVariantsBulkCreate(productId: $productId, variants: $variants) {
productVariants {
id
title
sku
price
}
userErrors {
field
message
}
}
}
```
**4. Update Variants (Bulk)**
```graphql
mutation BulkUpdateVariants($variants: [ProductVariantsBulkInput!]!) {
productVariantsBulkUpdate(variants: $variants) {
productVariants {
id
sku
price
}
userErrors {
message
}
}
}
```
### Order Management
**5. Get Orders (Paginated)**
```graphql
query GetOrders($first: Int!, $after: String) {
orders(first: $first, after: $after) {
edges {
node {
id
name
createdAt
customer {
id
email
}
lineItems(first: 10) {
edges {
node {
id
title
quantity
}
}
}
}
}
pageInfo {
hasNextPage
endCursor
}
}
}
```
**6. Update Order (Add Tags)**
```graphql
mutation UpdateOrderTags($input: OrderInput!) {
orderUpdate(input: $input) {
order {
id
tags
}
userErrors {
field
message
}
}
}
```
**7. Begin Order Edit**
```graphql
mutation BeginOrderEdit($orderId: ID!) {
orderEditBegin(orderId: $orderId) {
calculatedOrder {
id
lineItems(first: 10) {
edges {
node {
id
title
quantity
}
}
}
}
userErrors {
field
message
}
}
}
```
**8. Commit Order Edit**
```graphql
mutation CommitOrderEdit($id: ID!) {
orderEditCommit(id: $id) {
order {
id
name
}
userErrors {
field
message
}
}
}
```
### Fulfillment
**9. Create Fulfillment**
```graphql
mutation CreateFulfillment($input: FulfillmentInput!) {
fulfillmentCreate(input: $input) {
fulfillment {
id
status
trackingInfo {
number
company
url
}
}
userErrors {
field
message
}
}
}
```
### Inventory
**10. Adjust Inventory (Multiple Locations)**
```graphql
mutation AdjustInventory($input: InventoryAdjustQuantitiesInput!) {
inventoryAdjustQuantities(input: $input) {
inventoryLevels {
id
quantity
location {
id
name
}
}
userErrors {
field
message
}
}
}
```
### Customers
**11. Create Customer**
```graphql
mutation CreateCustomer($input: CustomerInput!) {
customerCreate(input: $input) {
customer {
id
email
firstName
lastName
}
userErrors {
field
message
}
}
}
```
**12. Update Customer**
```graphql
mutation UpdateCustomer($input: CustomerInput!) {
customerUpdate(input: $input) {
customer {
id
email
updatedAt
}
userErrors {
field
message
}
}
}
```
**13. Get Customer with Orders**
```graphql
query GetCustomerOrders($customerId: ID!, $first: Int!) {
customer(id: $customerId) {
id
email
firstName
orders(first: $first) {
edges {
node {
id
name
}
}
}
}
}
```
### Metafields
**14. Set Metafields (Product)**
```graphql
mutation SetMetafields($input: MetafieldsSetInput!) {
metafieldsSet(input: $input) {
metafields {
id
namespace
key
value
}
userErrors {
field
message
}
}
}
```
**15. Create Metaobject**
```graphql
mutation CreateMetaobject($input: MetaobjectInput!) {
metaobjectCreate(input: $input) {
metaobject {
id
type
fields {
key
value
}
}
userErrors {
field
message
}
}
}
```
### Discounts
**16. Create Discount Code**
```graphql
mutation CreateDiscount($input: DiscountCodeBasicInput!) {
discountCodeBasicCreate(input: $input) {
discountCodeBasic {
id
codes(first: 1) {
edges {
node {
code
}
}
}
}
userErrors {
field
message
}
}
}
```
### Webhooks
**17. Create Webhook Subscription**
```graphql
mutation CreateWebhook($input: WebhookSubscriptionInput!) {
webhookSubscriptionCreate(input: $input) {
webhookSubscription {
id
topic
endpoint {
__typename
... on WebhookHttpEndpoint {
callbackUrl
}
}
}
userErrors {
field
message
}
}
}
```
### Draft Orders
**18. Create Draft Order**
```graphql
mutation CreateDraftOrder($input: DraftOrderInput!) {
draftOrderCreate(input: $input) {
draftOrder {
id
invoiceUrl
}
userErrors {
field
message
}
}
}
```
**19. Complete Draft Order**
```graphql
mutation CompleteDraftOrder($id: ID!) {
draftOrderComplete(id: $id) {
draftOrder {
id
order {
id
name
}
}
userErrors {
field
message
}
}
}
```
### Shop
**20. Get Shop Details**
```graphql
query GetShopDetails {
shop {
id
name
email
myshopifyDomain
currency
}
}
```
### Collections
**21. Create Collection**
```graphql
mutation CreateCollection($input: CollectionInput!) {
collectionCreate(input: $input) {
collection {
id
handle
title
}
userErrors {
field
message
}
}
}
```
**22. Add Products to Collection**
```graphql
mutation AddProductsToCollection($id: ID!, $productIds: [ID!]!) {
collectionAddProducts(id: $id, productIds: $productIds) {
collection {
id
products(first: 10) {
totalCount
}
}
userErrors {
field
message
}
}
}
```
### Bulk Operations
**23. Run Bulk Query**
```graphql
mutation BulkQueryRun($query: String!) {
bulkOperationRunQuery(query: $query) {
bulkOperation {
id
status
createdAt
}
userErrors {
field
message
}
}
}
```
**24. Get Bulk Operation Status**
```graphql
query GetBulkOperation($id: ID!) {
node(id: $id) {
... on BulkOperation {
id
status
createdAt
completedAt
objectCount
fileSize
url
}
}
}
```
**25. Run Bulk Mutation**
```graphql
mutation BulkMutationRun($input: String!) {
bulkOperationRunMutation(input: $input) {
bulkOperation {
id
status
}
userErrors {
field
message
}
}
}
```
### Search
**26. Search Products**
```graphql
query SearchProducts($query: String!, $first: Int) {
products(first: $first, query: $query) {
edges {
node {
id
title
handle
}
}
}
}
```
### Locations
**27. Get All Locations**
```graphql
query GetLocations {
locations(first: 250) {
edges {
node {
id
name
isActive
}
}
}
}
```
### Refunds
**28. Create Refund**
```graphql
mutation CreateRefund($input: RefundInput!) {
refundCreate(input: $input) {
refund {
id
status
}
userErrors {
field
message
}
}
}
```
### Returns
**29. Create Return**
```graphql
mutation CreateReturn($input: ReturnInput!) {
returnCreate(input: $input) {
return {
id
status
requestedAt
}
userErrors {
field
message
}
}
}
```
### App Info
**30. Get App Installation Data**
```graphql
query GetAppInstallation {
appInstallation {
launchUrl
accessScopes {
handle
}
}
}
```
---
## Bulk Operations Workflow
For 100+ record operations, use bulk mutations to avoid rate limit delays:
```javascript
async function processBulkResults(fileUrl) {
const response = await fetch(fileUrl);
const text = await response.text();
const lines = text.trim().split('\n');
const results = lines.map(line => JSON.parse(line));
const errors = results.filter(r => r.__typename === 'Error');
if (errors.length > 0) {
console.error('Bulk operation errors:', errors);
}
return results.filter(r => r.__typename !== 'Error');
}
```
---
## User Errors Handling
```javascript
function handleMutationResponse(response) {
if (response.errors) {
throw new Error(`GraphQL Error: ${response.errors[0].message}`);
}
const result = response.data?.productCreate;
if (result.userErrors.length > 0) {
const fieldErrors = result.userErrors.map(err =>
`${err.field.join('.')}: ${err.message}`
).join('; ');
throw new Error(`Validation failed: ${fieldErrors}`);
}
return result.product;
}
```
---
## Global Resource Identifiers (GIDs)
All Shopify resources use format: `gid://shopify/{ResourceType}/{NumericID}`
**Common types:**
- `gid://shopify/Product/123456`
- `gid://shopify/Order/345678`
- `gid://shopify/Customer/901234`
- `gid://shopify/Location/567890`
**Parsing GIDs:**
```javascript
function parseGid(gid) {
const match = gid.match(/gid:\/\/shopify\/(\w+)\/(.+)/);
return {
type: match[1],
id: match[2],
};
}
```
---
## Scope-to-Operation Mapping
| Scope | Operations |
|-------|-----------|
| `write_products` | Create, update products; manage variants |
| `read_products` | Query products, variants, collections |
| `write_orders` | Update orders, create fulfillments |
| `read_orders` | Query orders, line items |
| `write_customers` | Create, update customers |
| `read_customers` | Query customer data |
| `write_inventory` | Adjust inventory quantities |
| `read_inventory` | Query inventory levels |
| `write_webhooks` | Create webhook subscriptions |
---
## MoneyV2 Fields Best Practice
Always request money values with currency:
```graphql
query {
products(first: 1) {
edges {
node {
priceRange {
minVariantPrice {
amount
currencyCode
}
}
variants(first: 1) {
edges {
node {
price
compareAtPrice
}
}
}
}
}
}
}
```
---
## Common Gotchas (15 Critical Issues)
| Gotcha | Solution |
|--------|----------|
| **userErrors but no message** | Check field array—sometimes indicates the issue |
| **Product not appearing in storefront** | Ensure `status: ACTIVE` is set |
| **Metafield value undefined** | Metafields must match namespace/key exactly |
| **Pagination returns empty with hasNextPage: true** | Use `after: endCursor`, not offset |
| **GID format errors** | Always use full format: `gid://shopify/Type/ID` |
| **Rate limited immediately** | Check query cost; use `first: 50` initially |
| **Variant options not syncing** | Product options must be created before variants |
| **Webhook never delivers** | Verify endpoint returns 200-299 status |
| **Customer metafields not visible** | Check `visible_to_storefront` flag |
| **Bulk operation returns partial results** | JSONL requires proper line breaks |
| **Order edit fails silently** | Must run `orderEditBegin` first |
| **Price not updating** | Use `variants` input as collection |
| **Inventory shows negative** | Shopify allows negatives; check location config |
| **Collection products order wrong** | Use `collectionReorderProducts` if order matters |
| **Webhook signature mismatch** | Use raw request body bytes for HMAC, not JSON |
---
## Decision Tree: Choosing the Right Operation
**Product Management:** Single product? Use create/update. Many variants? Use bulk variants.
**Order Processing:** Modify after creation? Use orderEditBegin/Commit. Add tags? Use orderUpdate.
**Inventory:** Single location? Use inventoryAdjustQuantities. Many locations? Use bulk mutation.
**Customers:** New customer? Use customerCreate. Attach data? Use metafieldsSet.
**Custom Data:** Attach to existing resource? Use metafieldsSet. Create new structure? Use metaobjectCreate.
---
## API Version & Support Lifecycle
- **Current when this release was audited:** 2026-07
- **Rule:** Confirm the latest stable version and its support dates before deployment
- **Release:** Quarterly (Jan, Apr, Jul, Oct)
- **Support:** 12 months per version
---
## Reference URLs
- [Shopify Admin GraphQL API Docs](https://shopify.dev/docs/api/admin-graphql/latest)
- [GraphQL Mutations Reference](https://shopify.dev/docs/api/admin-graphql/latest/mutations)
- [Rate Limiting Guide](https://shopify.dev/docs/api/usage/limits)
- [Global IDs Explained](https://shopify.dev/docs/api/usage/gids)
- [OAuth Scopes Reference](https://shopify.dev/docs/api/usage/access-scopes)
- [API Version Timeline](https://shopify.dev/api/admin-graphql#api-versions)
- [Bulk Operations Guide](https://shopify.dev/docs/api/usage/bulk-operations/queries)More API Design skills
lark-event
larksuite/cli
Lark/Feishu real-time event listening / subscribing / consuming: stream events as NDJSON via `lark-cli event consume <EventKey>` (covers IM messages/reactions/chat changes, Approval status changes, Task updates, VC meeting started/joined/ended, Minutes generated, Whiteboard updated, etc.). Use for Lark bots, real-time message processing, long-running subscribers, streaming webhook/push handlers. Supports `--max-events` / `--timeout` bounded runs and a stderr ready-marker contract — designed for AI agents running as subprocesses.
lark-contact
larksuite/cli
飞书 / Lark 通讯录:按姓名 / 邮箱解析成 open_id,或按 open_id 反查姓名 / 部门 / 邮箱 / 联系方式 / 个人状态 / 签名,以及按关键词搜索当前用户可见的机器人 / 智能体(agent)。当用户提到一个名字要下一步发消息 / 排日程,或拿到 open_id 想查具体信息时使用。不负责部门树遍历、按部门列员工、组织架构图,这类需求走原生 OpenAPI。
lark-openapi-explorer
larksuite/cli
飞书/Lark 原生 OpenAPI 探索:从官方文档库中挖掘未经 CLI 封装的原生 OpenAPI 接口。当用户的需求无法被现有 lark-* skill 或 lark-cli 已注册命令满足,需要查找并调用原生飞书 OpenAPI 时使用。

