
Same Day Delivery
- 89 installs
- 41 repo stars
- Updated March 13, 2026
- finsilabs/awesome-ecommerce-skills
Offer same-day local delivery with geographic zone management, customer-facing time-slot booking, and driver dispatch coordination.
About
Implements same-day local delivery via geographic delivery zones, customer time-slot booking, and driver dispatch coordination. A developer uses it to add local same-day fulfillment to a store.
- Geographic delivery-zone management with time-slot booking
- Driver dispatch coordination
Same Day Delivery by the numbers
- 89 all-time installs (skills.sh)
- Ranked #3,026 of 4,347 Backend & APIs skills by installs in the Skillselion catalog
- Data as of Aug 3, 2026 (Skillselion catalog sync)
npx skills add https://github.com/finsilabs/awesome-ecommerce-skills --skill same-day-deliveryAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 89 |
|---|---|
| repo stars | ★ 41 |
| Last updated | March 13, 2026 |
| Repository | finsilabs/awesome-ecommerce-skills ↗ |
What it does
Offer same-day local delivery with geographic zone management, customer-facing time-slot booking, and driver dispatch coordination.
Files
Same-Day Delivery
Overview
Same-day local delivery requires three things working together: a way for customers to select a delivery time slot at checkout, a way for you to dispatch orders to drivers, and a way to communicate delivery status back to customers. For most merchants, third-party last-mile services (DoorDash Drive, Uber Direct, Onfleet) are the fastest path to same-day delivery — they handle driver logistics so you focus on operations.
When to Use This Skill
- When launching a same-day or next-hour delivery service in a defined geographic area
- When allowing customers to select a preferred delivery window at checkout
- When building a driver dispatch dashboard that shows outstanding orders and optimizes routes
- When integrating with a third-party last-mile courier (DoorDash Drive, Uber Direct, Onfleet)
- When managing capacity limits per time slot to prevent over-committing delivery resources
Core Instructions
Step 1: Determine your platform and delivery model
| Platform | Recommended Tool | Why |
|---|---|---|
| Shopify | Local Delivery by Zapiet + DoorDash Drive or Uber Direct | Zapiet handles time-slot booking at checkout; DoorDash Drive / Uber Direct dispatch drivers automatically |
| WooCommerce | WooCommerce Local Pickup Plus + Onfleet or your own drivers | Local Pickup Plus handles zones and time slots; Onfleet provides driver dispatch and tracking |
| BigCommerce | Zapiet Delivery + Onfleet or Dispatch Science | Zapiet and similar apps add delivery scheduling; Dispatch Science optimizes routes |
| Custom / Headless | Build time-slot booking + integrate Onfleet/DoorDash Drive API for dispatch | Full control over zone management, slot capacity, and driver routing |
Choose your delivery model first:
- Your own drivers: You control quality and cost but must manage driver availability, vehicles, and routing. Best for: florists, grocery, restaurants with a regular local customer base.
- On-demand couriers (DoorDash Drive, Uber Direct): Drivers appear on demand with no fixed cost. Best for: merchants who need occasional same-day delivery without committing to a driver fleet.
- Hybrid: Your drivers for scheduled slots; on-demand couriers for rush orders.
Step 2: Set up delivery zones and time slot booking
Shopify
Using Zapiet — Pickup + Delivery (the most feature-complete app):
1. Install Zapiet — Pickup + Delivery from the Shopify App Store 2. In Zapiet, go to Delivery → Zones and define your delivery coverage area by:
- Postal/ZIP codes (simplest): list all ZIP codes you deliver to
- Radius from your store address
- Custom drawn polygon (requires higher Zapiet plan)
3. Set up time slots in Delivery → Time Slots:
- Define daily windows (e.g., 10am–12pm, 12pm–2pm, 2pm–4pm, 4pm–6pm)
- Set capacity per slot (e.g., max 20 orders per 2-hour window)
- Set the order cutoff time for each slot (e.g., orders for the 2pm–4pm slot must be placed by 12pm)
4. Set delivery fees per zone in Delivery → Rates (can be distance-based or flat) 5. Customers see available time slots during Shopify checkout after entering their address
Order management in Zapiet:
- Go to Zapiet → Orders to see all delivery orders sorted by time slot
- Export the pick list and manifest for your drivers from this view
- Mark orders as "out for delivery" and "delivered" to update customers
WooCommerce
Using WooCommerce Local Pickup Plus (WooCommerce.com official extension):
1. Install Local Pickup Plus from WooCommerce.com 2. Go to WooCommerce → Settings → Shipping → Local Pickup Plus 3. Add pickup/delivery locations — for delivery, define your service area by postal code or radius 4. Enable "Delivery date & time selection" to let customers pick slots 5. Configure time slots and capacity in the plugin settings
For more advanced zone management: use the Flexible Shipping plugin or WooCommerce Table Rate Shipping to create delivery rates based on postal code matching.
For driver dispatch: use Onfleet (onfleet.com) which has a WooCommerce webhook integration — new delivery orders automatically appear in Onfleet for driver assignment.
BigCommerce
Using Zapiet on BigCommerce: 1. Install Zapiet from the BigCommerce App Marketplace 2. Same configuration as the Shopify workflow above — define zones, time slots, and capacity
Using ShipperHQ:
- ShipperHQ has local delivery zones and time-window restrictions built in
- Go to ShipperHQ → Carrier Manager → Add Local Delivery carrier and define your zone rules
Step 3: Connect to a driver dispatch platform
Using DoorDash Drive (on-demand, no fixed driver costs)
DoorDash Drive sends DoorDash gig-economy drivers to pick up and deliver your orders. Available in most major US cities.
1. Sign up at developer.doordash.com/portal for a DoorDash Drive API key 2. For Shopify: install DoorDash Drive from the Shopify App Store — it creates a DoorDash delivery automatically when you mark an order for dispatch 3. DoorDash notifies the customer with a real-time tracking link via SMS 4. You pay per delivery (typically $7–$15 depending on distance)
Using Uber Direct (on-demand)
Similar to DoorDash Drive — Uber Direct uses Uber drivers for local delivery.
1. Sign up at developer.uber.com/products/uber-direct 2. Install the Uber Direct app if available for your platform, or use the REST API 3. Create a delivery by sending the pickup address (your store) and drop-off address (customer) to the Uber Direct API
Using Onfleet (your own drivers + route optimization)
Best if you have your own delivery team and need route optimization and real-time tracking.
1. Sign up at onfleet.com (starts at $349/month for up to 3 drivers) 2. Install the WooCommerce webhook integration or use Zapier to connect your platform to Onfleet 3. New orders auto-create Onfleet tasks 4. Dispatchers assign tasks to drivers in the Onfleet web dashboard 5. Drivers get a mobile app with turn-by-turn navigation and proof-of-delivery photo capture 6. Customers receive an SMS with a real-time tracking link when the driver starts their route
Step 4: Handle edge cases
Slot fills up after customer views it:
- Most apps (Zapiet, Onfleet scheduling) use real-time slot availability checks at checkout
- Enable slot capacity enforcement in your app settings to prevent overbooking
Address outside delivery zone:
- Zapiet checks the delivery zone before showing time slots — if the address is outside your zone, delivery options are hidden and only pickup/standard shipping shows
- Test this thoroughly with addresses near your zone boundary before going live
Driver can't fulfill an order:
- For DoorDash Drive / Uber Direct: the platform automatically reassigns to another driver
- For your own drivers (Onfleet): the dispatcher must manually reassign in the dashboard; set up Onfleet alerts for unassigned tasks approaching their slot window
Cutoff time management:
- Set your order cutoff 2–3 hours before the delivery window to give time for picking, packing, and loading
- Zapiet automatically hides time slots that have passed their cutoff
Step 5: Custom / Headless Implementation
For headless stores that need full control over zone management and slot booking:
// Check if a customer address is in a delivery zone
async function checkDeliveryEligibility(params: {
customerZip: string;
deliveryZones: { name: string; zipCodes: string[]; deliveryFeeCents: number }[];
}): Promise<{ eligible: boolean; zone?: string; feeCents?: number }> {
const zone = params.deliveryZones.find(z => z.zipCodes.includes(params.customerZip));
if (!zone) return { eligible: false };
return { eligible: true, zone: zone.name, feeCents: zone.deliveryFeeCents };
}
// Get available time slots for today (slots with remaining capacity)
async function getAvailableSlots(params: {
date: Date;
zone: string;
slots: { id: string; label: string; capacity: number; booked: number; cutoffTime: Date }[];
}): Promise<{ id: string; label: string; spotsRemaining: number }[]> {
const now = new Date();
return params.slots
.filter(slot => slot.cutoffTime > now && slot.booked < slot.capacity)
.map(slot => ({
id: slot.id,
label: slot.label,
spotsRemaining: slot.capacity - slot.booked,
}));
}
// Dispatch a delivery via DoorDash Drive API
async function dispatchDoorDashDelivery(params: {
externalDeliveryId: string;
pickupAddress: Address;
dropoffAddress: Address;
customerPhone: string;
pickupWindow: { startTime: string; endTime: string }; // ISO 8601
}): Promise<{ trackingUrl: string; fee: number }> {
const response = await fetch('https://openapi.doordash.com/drive/v2/deliveries', {
method: 'POST',
headers: {
'Authorization': `Bearer ${process.env.DOORDASH_DRIVE_JWT}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
external_delivery_id: params.externalDeliveryId,
pickup_address: `${params.pickupAddress.street1}, ${params.pickupAddress.city}, ${params.pickupAddress.state} ${params.pickupAddress.zip}`,
dropoff_address: `${params.dropoffAddress.street1}, ${params.dropoffAddress.city}, ${params.dropoffAddress.state} ${params.dropoffAddress.zip}`,
dropoff_phone_number: params.customerPhone,
pickup_time: params.pickupWindow.startTime,
}),
});
const data = await response.json();
return { trackingUrl: data.tracking_url, fee: data.fee };
}Best Practices
- Start with ZIP code zones, not radius or polygon — ZIP-code-based zones are easier to manage, easier to communicate to customers ("We deliver to these zip codes"), and don't require geocoding
- Set slot cutoff times generously — give your warehouse at least 2 hours between order cutoff and window start to pick, pack, and hand off to drivers
- Generate delivery manifests before each slot window — print the manifest 30 minutes before your driver leaves; it should list each order with address, time slot, and special instructions
- Send ETA push notifications as the driver approaches — customers with live ETAs submit far fewer "where is my delivery" support contacts; Onfleet and DoorDash Drive both handle this automatically
- Monitor slot fill rates — if slots fill up consistently hours before the window, add capacity; if they're under 50% filled at cutoff, consolidate or reduce windows
Common Pitfalls
| Problem | Solution |
|---|---|
| Two customers book the last spot in a slot simultaneously | Use atomic slot decrement with capacity check — Zapiet handles this; for custom builds use database-level locks or atomic updates |
| Customer enters an address outside the delivery zone but sees time slots | Validate zone eligibility server-side at checkout, not just client-side; Zapiet enforces this automatically |
| Cutoff time displayed in wrong timezone for customer | Always store and compare times in UTC; display to customers in their local timezone using the browser's Intl API |
| Driver assigned to more orders than they can fulfill in the window | Cap orders per driver per slot and enable route optimization in Onfleet; set realistic capacity limits when building your slot schedule |
Related Skills
- @order-fulfillment-workflow
- @shipment-tracking
- @order-management-system
- @international-shipping
- @dropshipping-integration
{
"context": "Tests whether the agent implements slot booking with proper concurrency control — using database-level row locking and a single atomic transaction — and correctly handles the three failure conditions (slot inactive, slot full, cutoff passed) while incrementing the booked count safely.",
"type": "weighted_checklist",
"checklist": [
{
"name": "Transaction wrapping",
"max_score": 10,
"description": "The entire booking operation (read slot, validate, increment booked, create assignment) is wrapped in a single database transaction"
},
{
"name": "SELECT FOR UPDATE",
"max_score": 14,
"description": "The slot row is fetched inside the transaction using SELECT ... FOR UPDATE (or equivalent advisory/row lock) before validation and update"
},
{
"name": "Capacity check in transaction",
"max_score": 12,
"description": "The check booked >= capacity (or booked < capacity) is performed INSIDE the transaction, after the lock is acquired — not before the transaction begins"
},
{
"name": "Cutoff check in transaction",
"max_score": 10,
"description": "The check that current time has not passed cutoff_time is performed INSIDE the transaction"
},
{
"name": "is_active check",
"max_score": 8,
"description": "The slot's is_active field is checked inside the transaction and booking is rejected if the slot is not active"
},
{
"name": "Atomic booked increment",
"max_score": 14,
"description": "The booked count is incremented with a SQL expression (e.g. booked = booked + 1) rather than reading the current value in application code and writing back a calculated number"
},
{
"name": "Assignment record created",
"max_score": 10,
"description": "A delivery_assignments row with status 'pending' is inserted inside the same transaction as the booked increment"
},
{
"name": "SLOT_FULL error raised",
"max_score": 8,
"description": "A clear error (exception, rejected promise, or error response) is thrown/returned when booked >= capacity"
},
{
"name": "SLOT_CUTOFF_PASSED error raised",
"max_score": 8,
"description": "A clear error is thrown/returned when the current time is past cutoff_time"
},
{
"name": "No pre-transaction validation",
"max_score": 6,
"description": "The implementation does NOT do a capacity or cutoff check outside the transaction (e.g. an unprotected findById before opening the transaction) as the sole guard"
}
]
}
Time-Slot Booking Service: Handling the Last-Seat Race Condition
Problem/Feature Description
An online florist runs a same-day delivery service with limited capacity per two-hour delivery window. On popular days (Valentine's Day, Mother's Day) hundreds of customers reach the checkout simultaneously, and several of them are often competing for the last one or two spots in a popular afternoon slot. The current implementation reads the slot availability, checks it in application code, and then inserts a booking — a pattern that has caused multiple incidents where more orders were confirmed than the slot could actually handle.
The engineering team needs a robust booking function that is safe to call concurrently from many web server processes. The function must reject bookings cleanly with informative errors when a slot is full, when the booking window has closed (past cutoff), or when the slot has been deactivated by an admin. The solution must work correctly even if two requests arrive at the exact same millisecond.
Output Specification
Produce the following files:
1. book-slot.ts — TypeScript module exporting a bookDeliverySlot(orderId: string, slotId: string): Promise<DeliveryAssignment> function that handles concurrent booking safely.
2. book-slot.test.ts — Unit/integration test file (using any test framework) with at least three test cases covering: successful booking, rejection when slot is full, and rejection when past cutoff.
3. NOTES.md — A short explanation of the concurrency strategy used and why it prevents double-booking.
You may define or stub db, DeliverySlot, and DeliveryAssignment types as needed. The implementation does not need to connect to a real database, but the SQL and transaction logic must be written correctly as if it would run against PostgreSQL.
{
"context": "Tests whether the agent correctly implements driver dispatch with push notifications, real-time ETA updates via WebSocket, a heartbeat check for GPS loss, delivery completion side-effects, and a failure recovery flow that reschedules the customer.",
"type": "weighted_checklist",
"checklist": [
{
"name": "Push notification on assignment",
"max_score": 8,
"description": "When a driver is assigned, a push notification is sent to the driver containing at minimum: the order identifier (order number or ID) and the delivery address"
},
{
"name": "Notification includes assignmentId",
"max_score": 6,
"description": "The push notification payload includes assignmentId (and/or orderId) in a data/metadata field"
},
{
"name": "WebSocket update on status change",
"max_score": 10,
"description": "Every call to update delivery status pushes a real-time message to the customer (e.g. via websocketHub.sendToOrder or equivalent), including the new status and ETA"
},
{
"name": "ETA update interval 2-3 minutes",
"max_score": 10,
"description": "The design or code specifies that ETA is recalculated and pushed every 2–3 minutes (or a value in the 2–3 minute range) while the driver is en_route — not less frequently or only once"
},
{
"name": "ETA computed from driver location",
"max_score": 8,
"description": "ETA is estimated using the driver's current latitude and longitude (passed as parameters or sourced from a location update), not a static value"
},
{
"name": "Heartbeat threshold 5 minutes",
"max_score": 10,
"description": "A heartbeat or watchdog mechanism alerts dispatch if no driver GPS update is received for 5 minutes (not a different threshold) while the assignment is en_route"
},
{
"name": "Heartbeat scope: en_route only",
"max_score": 6,
"description": "The heartbeat/watchdog check is only triggered when the assignment status is en_route, not for all statuses"
},
{
"name": "delivered_at set on completion",
"max_score": 8,
"description": "When status is updated to 'delivered', the delivered_at timestamp is set on the delivery_assignments record"
},
{
"name": "Order status updated on delivery",
"max_score": 8,
"description": "When status is 'delivered', the associated order record is also updated to status 'delivered'"
},
{
"name": "Confirmation email on delivery",
"max_score": 8,
"description": "When status is 'delivered', a delivery confirmation email is sent (e.g. sendDeliveryConfirmationEmail or equivalent)"
},
{
"name": "Failed: customer reschedule offered",
"max_score": 9,
"description": "When a driver reports 'failed', the system offers (or triggers) a reschedule for the customer to the next available slot"
},
{
"name": "Failed: re-assignment triggered",
"max_score": 9,
"description": "When a driver reports 'failed', the system also triggers a driver re-assignment (not just a customer notification)"
}
]
}
Driver Dispatch Module: Assignment, Live Tracking, and Failure Recovery
Problem/Feature Description
A meal-kit delivery startup has built out zone management and slot booking, and now needs the driver-facing side of the platform. When an operations coordinator assigns a driver to an order, the driver must receive an immediate notification on their mobile app. Once the driver starts heading to the customer, the platform needs to keep the customer informed with live ETAs so they know when to expect their delivery — the support team has found that customers without live updates generate three times as many "where is my order?" contacts.
The platform also needs to handle two failure modes gracefully. Drivers sometimes lose mobile connectivity while en route, and dispatch needs an automated alert before too much time passes without a location ping. And on rare occasions a driver cannot complete a delivery; in that case the customer should automatically be offered an alternative window and a new driver should be queued up, rather than leaving the order in limbo.
Output Specification
Produce the following files:
1. dispatch.ts — TypeScript module exporting:
assignDriverToOrder(assignmentId: string, driverId: string): Promise<void>— assigns a driver and notifies them.updateDeliveryStatus(assignmentId: string, status: 'en_route' | 'delivered' | 'failed', driverLat?: number, driverLng?: number): Promise<void>— updates assignment status and handles all side-effects for each status.
2. driver-heartbeat.ts — TypeScript module that exports a function (or class) implementing a heartbeat/watchdog mechanism for active deliveries. Include a comment explaining the threshold used and why.
3. NOTES.md — Explanation of the ETA update strategy (frequency, data source) and the failure-recovery flow for a failed delivery.
You may stub db, pushNotification, websocketHub, and any external services. Focus on the logic and the correctness of the side-effects for each scenario. The implementation does not need to be runnable.
{
"context": "Tests whether the agent correctly models delivery zones using PostGIS polygon geometry with appropriate indexing, creates a correct slot schema with uniqueness constraints, and implements zone lookup using the correct spatial query with the right coordinate argument order.",
"type": "weighted_checklist",
"checklist": [
{
"name": "PostGIS polygon type",
"max_score": 12,
"description": "The delivery_zones table stores the zone geometry as GEOGRAPHY(POLYGON, 4326) or GEOMETRY(POLYGON, 4326), not as a bounding box (e.g. min_lat/max_lat/min_lng/max_lng columns) or a radius/circle approximation"
},
{
"name": "GIST index on polygon",
"max_score": 8,
"description": "A GIST index is created on the polygon/geometry column of the zones table (e.g. USING GIST(polygon))"
},
{
"name": "ST_Contains or equivalent spatial query",
"max_score": 10,
"description": "The zone lookup query uses ST_Contains (or ST_Within) with the polygon column and a constructed point, rather than a bounding-box comparison or radius check"
},
{
"name": "Coordinate order: lng before lat",
"max_score": 12,
"description": "The ST_MakePoint (or equivalent) call passes longitude as the first argument and latitude as the second — i.e. ST_MakePoint(lng, lat) — matching PostGIS convention"
},
{
"name": "SRID set to 4326",
"max_score": 8,
"description": "The constructed point is wrapped with ST_SetSRID(..., 4326) or the geometry column is declared with SRID 4326"
},
{
"name": "Unique slot index",
"max_score": 10,
"description": "A UNIQUE constraint or UNIQUE INDEX covers (zone_id, slot_date, window_start) on the delivery_slots table to prevent duplicate slot generation"
},
{
"name": "Status CHECK constraint",
"max_score": 8,
"description": "The delivery_assignments table includes a CHECK constraint on the status column that contains at least: 'pending', 'assigned', 'en_route', 'delivered', 'failed'"
},
{
"name": "Zone re-validation at checkout",
"max_score": 10,
"description": "The checkout or order-placement code re-calls the zone lookup function (or equivalent) to confirm the address is still within a valid zone, rather than trusting a zone_id passed from the client or stored only at address entry"
},
{
"name": "Available-slot filter criteria",
"max_score": 12,
"description": "The query or filter for listing available slots checks all three conditions: is_active = true, cutoff_time > now (current time), and booked < capacity"
},
{
"name": "No bounding-box fallback",
"max_score": 10,
"description": "The implementation does NOT use latitude/longitude min/max range comparisons or a radius (ST_DWithin / distance check) as the primary zone containment test"
}
]
}
Local Delivery Coverage: Zone Database and Address Eligibility API
Problem/Feature Description
A regional grocery chain is launching a same-day delivery service across three urban neighborhoods. The operations team has drawn precise coverage boundaries for each neighborhood on a map — irregular polygons that follow streets and postal boundaries rather than neat rectangles. The engineering team needs to build the persistence layer that stores these zone shapes and a backend function that, given any delivery address, determines which zone (if any) covers it.
Previous attempts used a simple lat/lng bounding box per zone, which caused edge cases where addresses near zone boundaries were incorrectly accepted or rejected. The team wants an exact, geometry-based solution that can handle overlapping zones (where the higher-minimum-order zone should be preferred) and is fast enough to run on every checkout request without a full table scan.
The product team also needs the checkout API to re-confirm that an address is still within a valid zone at the moment an order is placed, since customers sometimes navigate back and change their address mid-session.
Output Specification
Produce the following files:
1. schema.sql — SQL DDL for the delivery zone and slot tables, including all indexes and constraints needed for correctness and query performance.
2. zone-lookup.ts — TypeScript module exporting:
findDeliveryZone(lat: number, lng: number): Promise<DeliveryZone | null>— returns the best matching active zone for a coordinate pair, or null if none covers it.checkoutZoneValidation(orderId: string, lat: number, lng: number): Promise<DeliveryZone>— called during order placement to confirm the address is still in a valid zone; throws if it is not.
3. NOTES.md — brief explanation of the geometry approach chosen and why it is preferable to alternatives.
You may define a db object or import stubs as needed; the actual database connection does not need to be runnable. Focus on correctness of the queries and schema.
{
"name": "finsi/same-day-delivery",
"version": "0.1.0",
"summary": "Local delivery zone management, time-slot booking, and driver dispatch",
"skills": {
"same-day-delivery": {
"path": "SKILL.md"
}
}
}