Skip to main content
Glama

refund_order

Refund specific line items or shipping costs on a Shopify order. Choose restock behavior per item: no restock, cancel, or return to location. Optionally send customer notification. Returns refund GID and total.

Instructions

Issue a refund against an order — for specific line items (with quantities and optional restock behaviour), for shipping, or both. Returns the new refund's GID and total amount refunded. To refund a full order use cancel_order with refund=true instead (one-step). Use this tool when refunding partially: just one item, just shipping, an adjustment without item breakdown, or a return that needs explicit restock-to-location handling. The restockType per line item controls inventory behaviour: NO_RESTOCK (default — the items aren't coming back), CANCEL (restock as if cancelled), RETURN (restock with a return record at the given locationId). Pass notify: true to email the customer.

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
idYesOrder GID or numeric ID to refund.
refundLineItemsNoSpecific line items to refund with quantities. Omit to do a refund without item-level breakdown (use for shipping-only or adjustment refunds).
shippingNoRefund part or all of shipping. Pass {fullRefund: true} to refund everything paid in shipping; or {amount: '5.00'} for a specific amount.
currencyNoISO currency code. Required for multi-currency stores; defaults to the order's currency otherwise.
noteNoInternal note explaining the refund.
notifyNoEmail the customer a refund notification.

Implementation Reference

  • The tool handler function for 'refund_order'. Registered via server.tool() with name 'refund_order'. Accepts args validated by refundOrderSchema, calls the REFUND_CREATE_MUTATION GraphQL mutation, and returns the refund GID and total refunded amount.
    server.tool(
      "refund_order",
      "Issue a refund against an order — for specific line items (with quantities and optional restock behaviour), for shipping, or both. Returns the new refund's GID and total amount refunded. To refund a full order use cancel_order with refund=true instead (one-step). Use this tool when refunding partially: just one item, just shipping, an adjustment without item breakdown, or a return that needs explicit restock-to-location handling. The `restockType` per line item controls inventory behaviour: NO_RESTOCK (default — the items aren't coming back), CANCEL (restock as if cancelled), RETURN (restock with a return record at the given locationId). Pass `notify: true` to email the customer.",
      refundOrderSchema,
      async (args) => {
        const input: Record<string, unknown> = {
          orderId: toGid(args.id, "Order"),
          notify: args.notify,
        };
        if (args.refundLineItems) {
          input.refundLineItems = args.refundLineItems.map((li) => ({
            lineItemId: li.lineItemId,
            quantity: li.quantity,
            restockType: li.restockType,
            locationId: li.locationId,
          }));
        }
        if (args.shipping) input.shipping = args.shipping;
        if (args.currency) input.currency = args.currency;
        if (args.note) input.note = args.note;
    
        const data = await client.graphql<{
          refundCreate: {
            refund: {
              id: string;
              totalRefundedSet: {
                shopMoney: { amount: string; currencyCode: string };
              };
              note?: string | null;
            } | null;
            userErrors: ShopifyUserError[];
          };
        }>(REFUND_CREATE_MUTATION, { input });
        throwIfUserErrors(data.refundCreate.userErrors, "refundCreate");
        const r = data.refundCreate.refund;
        if (!r) {
          return {
            content: [
              { type: "text" as const, text: "refundCreate returned no refund." },
            ],
          };
        }
        const total = `${r.totalRefundedSet.shopMoney.amount} ${r.totalRefundedSet.shopMoney.currencyCode}`;
        return {
          content: [
            {
              type: "text" as const,
              text: `Refunded ${total} on ${args.id} — refund ${r.id}`,
            },
          ],
        };
      },
    );
  • The input schema (refundOrderSchema) for the 'refund_order' tool. Defines fields: id (order GID/numeric ID), refundLineItems (array of line items with quantities and restock options), shipping (full refund or specific amount), currency, note, and notify (email notification).
    const refundOrderSchema = {
      id: z
        .string()
        .describe("Order GID or numeric ID to refund."),
      refundLineItems: z
        .array(refundLineItemSchema)
        .optional()
        .describe(
          "Specific line items to refund with quantities. Omit to do a refund without item-level breakdown (use for shipping-only or adjustment refunds).",
        ),
      shipping: z
        .object({
          fullRefund: z.boolean().optional(),
          amount: z.string().optional().describe("Specific shipping refund amount as decimal string."),
        })
        .optional()
        .describe(
          "Refund part or all of shipping. Pass {fullRefund: true} to refund everything paid in shipping; or {amount: '5.00'} for a specific amount.",
        ),
      currency: z
        .string()
        .optional()
        .describe(
          "ISO currency code. Required for multi-currency stores; defaults to the order's currency otherwise.",
        ),
      note: z.string().optional().describe("Internal note explaining the refund."),
      notify: z
        .boolean()
        .default(false)
        .describe("Email the customer a refund notification."),
    };
  • The refundLineItemSchema used within refundOrderSchema. Defines lineItemId, quantity, restockType (NO_RESTOCK/CANCEL/RETURN), and locationId for per-item refund details.
    const refundLineItemSchema = z.object({
      lineItemId: z.string().describe("LineItem GID from the order."),
      quantity: z.number().int().min(1).describe("How many of this line item to refund."),
      restockType: z
        .enum(["NO_RESTOCK", "CANCEL", "RETURN"])
        .default("NO_RESTOCK")
        .describe(
          "How to handle inventory: NO_RESTOCK (don't restock), CANCEL (restock as if cancelled), RETURN (restock as a return).",
        ),
      locationId: z
        .string()
        .optional()
        .describe("Location GID to restock to (required when restockType is CANCEL or RETURN)."),
    });
  • Registration of the 'refund_order' tool via server.tool() call inside registerOrderTools() function. Called from src/server.ts:58.
    server.tool(
      "refund_order",
      "Issue a refund against an order — for specific line items (with quantities and optional restock behaviour), for shipping, or both. Returns the new refund's GID and total amount refunded. To refund a full order use cancel_order with refund=true instead (one-step). Use this tool when refunding partially: just one item, just shipping, an adjustment without item breakdown, or a return that needs explicit restock-to-location handling. The `restockType` per line item controls inventory behaviour: NO_RESTOCK (default — the items aren't coming back), CANCEL (restock as if cancelled), RETURN (restock with a return record at the given locationId). Pass `notify: true` to email the customer.",
      refundOrderSchema,
      async (args) => {
        const input: Record<string, unknown> = {
          orderId: toGid(args.id, "Order"),
          notify: args.notify,
        };
        if (args.refundLineItems) {
          input.refundLineItems = args.refundLineItems.map((li) => ({
            lineItemId: li.lineItemId,
            quantity: li.quantity,
            restockType: li.restockType,
            locationId: li.locationId,
          }));
        }
        if (args.shipping) input.shipping = args.shipping;
        if (args.currency) input.currency = args.currency;
        if (args.note) input.note = args.note;
    
        const data = await client.graphql<{
          refundCreate: {
            refund: {
              id: string;
              totalRefundedSet: {
                shopMoney: { amount: string; currencyCode: string };
              };
              note?: string | null;
            } | null;
            userErrors: ShopifyUserError[];
          };
        }>(REFUND_CREATE_MUTATION, { input });
        throwIfUserErrors(data.refundCreate.userErrors, "refundCreate");
        const r = data.refundCreate.refund;
        if (!r) {
          return {
            content: [
              { type: "text" as const, text: "refundCreate returned no refund." },
            ],
          };
        }
        const total = `${r.totalRefundedSet.shopMoney.amount} ${r.totalRefundedSet.shopMoney.currencyCode}`;
        return {
          content: [
            {
              type: "text" as const,
              text: `Refunded ${total} on ${args.id} — refund ${r.id}`,
            },
          ],
        };
      },
    );
  • The REFUND_CREATE_MUTATION GraphQL mutation used by the refund_order handler to perform the refund via the Shopify Admin API.
    const REFUND_CREATE_MUTATION = /* GraphQL */ `
      mutation RefundCreate($input: RefundInput!) {
        refundCreate(input: $input) {
          refund {
            id
            totalRefundedSet { shopMoney { amount currencyCode } }
            note
          }
          userErrors { field message }
        }
      }
    `;
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations, description carries full burden. Discloses return value, restockType inventory behavior (including location requirement), and notify option. Lacks explicit statement of side effects or permissions, but sufficiently covers key behavioral traits.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Description is comprehensive but slightly lengthy. Information is front-loaded with core purpose and return value. Every sentence adds value, though some consolidation could improve conciseness.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given tool complexity (nested objects, multiple refund types, restock logic) and no output schema, description thoroughly covers all scenarios: line items with restock, shipping refund, adjustments, and customer notification. No gaps for an agent to misuse.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters5/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100%, but description adds significant value: explains restockType enums beyond values, provides examples for shipping object, clarifies when to omit refundLineItems, and notes default behavior. This goes beyond schema description.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

Description clearly states 'Issue a refund against an order' with explicit scope (specific line items, shipping, both) and return value (GID, total amount). Distinguishes from sibling cancel_order for full refunds.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Explicitly states when to use this tool (partial refunds) and alternative cancel_order for full refunds. Provides concrete use cases: one item, shipping, adjustment, return with restock-to-location.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Install Server

Other Tools

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/miller-joe/shopify-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server