book_shipment
Book a parcel delivery with APC Overnight. Returns a 22-digit waybill used for label retrieval and tracking.
Instructions
Book a parcel delivery with APC Overnight. Returns a 22-digit waybill used for label retrieval and tracking.
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| service | Yes | APC delivery service. Pass a friendly key (e.g. "next-day", "saturday-1200", "courier-pack", "liquid-0900", "ireland-road") or the raw ProductCode (e.g. "ND16", "NS12", "CP16", "LP09", "ROAD"). Call list_services for the full catalogue. Defaults to ND16 (standard next day by 16:00) if omitted. | |
| collectionDate | Yes | Collection date. YYYY-MM-DD or DD/MM/YYYY | |
| readyAt | No | Time goods will be ready HH:MM (default 09:00) | |
| closedAt | No | Time business closes HH:MM (default 17:00) | |
| numberOfPieces | Yes | Number of parcels/items in this consignment | |
| totalWeightKg | Yes | Total weight in kg | |
| itemType | No | Type of goods being sent | PARCEL |
| goodsValue | No | Declared value in GBP | |
| goodsDescription | No | Brief description of goods | |
| sender | Yes | Sender / collection address | |
| recipient | Yes | Recipient / delivery address | |
| reference | No | Your internal order or job reference |
Implementation Reference
- src/index.js:56-115 (registration)Registration of the 'book_shipment' tool via server.tool() with its Zod schema for input validation (service, collectionDate, numberOfPieces, totalWeightKg, itemType, sender/recipient addresses, etc.). The handler calls apc.createConsignment(params) and returns the waybill, orderNumber, productCode and other booking details.
server.tool( 'book_shipment', 'Book a parcel delivery with APC Overnight. Returns a 22-digit waybill used for label retrieval and tracking.', { service: z.string().describe( 'APC delivery service. Pass a friendly key (e.g. "next-day", "saturday-1200", ' + '"courier-pack", "liquid-0900", "ireland-road") or the raw ProductCode (e.g. "ND16", ' + '"NS12", "CP16", "LP09", "ROAD"). Call list_services for the full catalogue. ' + 'Defaults to ND16 (standard next day by 16:00) if omitted.' ), collectionDate: z.string().describe('Collection date. YYYY-MM-DD or DD/MM/YYYY'), readyAt: z.string().optional().describe('Time goods will be ready HH:MM (default 09:00)'), closedAt: z.string().optional().describe('Time business closes HH:MM (default 17:00)'), numberOfPieces: z.number().int().min(1).describe('Number of parcels/items in this consignment'), totalWeightKg: z.number().positive().describe('Total weight in kg'), itemType: z.enum(['PARCEL', 'PACK', 'LIQUIDS', 'LIMITED QUANTITIES']) .default('PARCEL') .describe('Type of goods being sent'), goodsValue: z.number().optional().describe('Declared value in GBP'), goodsDescription: z.string().optional().describe('Brief description of goods'), sender: addressSchema.describe('Sender / collection address'), recipient: addressSchema.describe('Recipient / delivery address'), reference: z.string().optional().describe('Your internal order or job reference'), }, async (params) => { try { const result = await apc.createConsignment(params); return { content: [{ type: 'text', text: JSON.stringify({ success: result.success, carrier: 'APC Overnight', waybill: result.waybill, orderNumber: result.orderNumber, productCode: result.productCode, collectionDate: params.collectionDate, service: params.service, pieces: params.numberOfPieces, weightKg: params.totalWeightKg, recipient: `${params.recipient.contactName}, ${params.recipient.postcode}`, note: 'Wait 3-5 seconds before calling get_label with the waybill.', }, null, 2), }], }; } catch (err) { return { content: [{ type: 'text', text: `Error booking shipment: ${err.message}` }], isError: true, }; } } ); - src/index.js:42-54 (schema)The 'addressSchema' Zod object used by both the sender and recipient parameters in the book_shipment tool, defining fields like companyName, contactName, addressLine1, city, postcode, phone, email, etc.
const addressSchema = z.object({ companyName: z.string().optional().describe('Company name (optional for collection if using account address)'), contactName: z.string().describe('Contact person name'), addressLine1: z.string().describe('First line of address'), addressLine2: z.string().optional().describe('Second line of address (optional)'), city: z.string().describe('Town or city'), county: z.string().optional().describe('County (optional)'), postcode: z.string().describe('UK postcode. Include the space e.g. WS11 8LD'), phone: z.string().describe('Phone number'), mobilePhone: z.string().optional().describe('Mobile number for delivery notifications (optional)'), email: z.string().optional().describe('Email address (optional)'), instructions: z.string().optional().describe('Delivery instructions e.g. leave with neighbour (recipient only)'), }); - src/carriers/apc.js:274-347 (handler)The actual handler function 'createConsignment' that performs the APC API call to book a shipment. It builds the full APC order JSON body (collection/delivery addresses, goods info, shipment details with items), POSTs to /Orders.json, and returns success, waybill, orderNumber, productCode.
export async function createConsignment(params) { const productCode = APC_SERVICES[params.service] || params.service || 'ND16'; const body = { Orders: { Order: { CollectionDate: toApcDate(params.collectionDate), ReadyAt: params.readyAt || '09:00', ClosedAt: params.closedAt || '17:00', ProductCode: productCode, Reference: params.reference || '', Collection: { CompanyName: params.sender.companyName || '', AddressLine1: params.sender.addressLine1, AddressLine2: params.sender.addressLine2 || '', PostalCode: params.sender.postcode, City: params.sender.city || params.sender.town || '', County: params.sender.county || '', CountryCode: 'GB', Contact: { PersonName: params.sender.contactName, PhoneNumber: params.sender.phone, Email: params.sender.email || null, }, }, Delivery: { CompanyName: params.recipient.companyName || '', AddressLine1: params.recipient.addressLine1, AddressLine2: params.recipient.addressLine2 || '', PostalCode: params.recipient.postcode, City: params.recipient.city || params.recipient.town || '', County: params.recipient.county || '', CountryCode: 'GB', Contact: { PersonName: params.recipient.contactName, PhoneNumber: params.recipient.phone, MobileNumber: params.recipient.mobilePhone || null, Email: params.recipient.email || null, }, Instructions: params.recipient.instructions || null, }, GoodsInfo: { GoodsValue: String(params.goodsValue || '1'), GoodsDescription: params.goodsDescription || 'Goods', Fragile: 'false', Security: 'false', IncreasedLiability: 'false', }, ShipmentDetails: { NumberOfPieces: String(params.numberOfPieces || 1), Items: { Item: buildItems(params), }, }, }, }, }; const result = await request('POST', '/Orders.json', body); const order = result?.Orders?.Order; return { success: result?.Orders?.Messages?.Code === 'SUCCESS', waybill: order?.WayBill, orderNumber: order?.OrderNumber, productCode: order?.ProductCode, collectionDate: order?.CollectionDate, raw: result, }; } - src/carriers/apc.js:145-178 (helper)The 'buildItems' helper function used by createConsignment to construct the Items.Item array for the APC order payload, splitting total weight across pieces and handling per-item arrays.
function buildItems(params) { const count = Math.max(1, parseInt(params.numberOfPieces, 10) || 1); const type = params.itemType || 'PARCEL'; const refBase = params.reference || ''; if (Array.isArray(params.items) && params.items.length > 0) { const built = params.items.map((it, i) => ({ Type: it.type || type, Weight: String(it.weightKg ?? 1), Length: String(it.lengthCm ?? '0'), Width: String(it.widthCm ?? '0'), Height: String(it.heightCm ?? '0'), Value: String(it.value ?? params.goodsValue ?? '1'), Reference: it.reference || (refBase ? `${refBase}-${i + 1}` : undefined), })); return built.length === 1 ? built[0] : built; } const total = parseFloat(params.totalWeightKg) || 1; const per = (total / count); const items = []; for (let i = 0; i < count; i++) { items.push({ Type: type, Weight: per.toFixed(3), Length: String(params.lengthCm || '0'), Width: String(params.widthCm || '0'), Height: String(params.heightCm || '0'), Value: String(params.goodsValue || '1'), Reference: refBase ? `${refBase}-${i + 1}` : undefined, }); } return items.length === 1 ? items[0] : items; } - src/carriers/apc.js:128-133 (helper)The 'toApcDate' helper that converts ISO date format (YYYY-MM-DD) to APC's required DD/MM/YYYY format.
function toApcDate(isoDate) { if (!isoDate) return null; if (/^\d{2}\/\d{2}\/\d{4}$/.test(isoDate)) return isoDate; const [y, m, d] = isoDate.split('-'); return `${d}/${m}/${y}`; }