<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: SendRelay</title>
    <description>The latest articles on DEV Community by SendRelay (@sendrelay).</description>
    <link>https://dev.to/sendrelay</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4146061%2Fce28bc45-4065-4e23-b17c-f54175a69671.png</url>
      <title>DEV Community: SendRelay</title>
      <link>https://dev.to/sendrelay</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/sendrelay"/>
    <language>en</language>
    <item>
      <title>Build the e-hailing app. Let Relay connect the drivers.</title>
      <dc:creator>SendRelay</dc:creator>
      <pubDate>Thu, 22 Oct 2026 16:17:07 +0000</pubDate>
      <link>https://dev.to/sendrelay-ng/build-the-e-hailing-app-let-relay-connect-the-drivers-1n3e</link>
      <guid>https://dev.to/sendrelay-ng/build-the-e-hailing-app-let-relay-connect-the-drivers-1n3e</guid>
      <description>&lt;p&gt;You can build a good booking app and still have nobody to send when a customer requests a ride.&lt;/p&gt;

&lt;p&gt;That is the awkward part of starting an e-hailing business as a solo developer. The map works. Payments work. You have spent weeks getting the pickup flow right. But before anyone can use it, you need drivers who will actually turn up.&lt;/p&gt;

&lt;p&gt;You could recruit them yourself. Find car owners, speak to fleet operators, work out who covers which area and start taking bookings. You do not have to own the vehicles, but you do have to organise the supply behind your app. That can quickly become the bigger job.&lt;/p&gt;

&lt;p&gt;Relay gives you a way to build around an existing driver network. It provides logistics as a service: your backend requests a task through the API, and Relay coordinates matching and task progress. You build the app your customers use. E-hailing is one use case for that infrastructure, rather than a passenger service operated by Relay itself.&lt;/p&gt;

&lt;h2&gt;
  
  
  What running your own fleet involves
&lt;/h2&gt;

&lt;p&gt;Say you want to launch in one part of Lagos. Before you can promise customers a pickup, there is quite a bit to arrange:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Recruitment.&lt;/strong&gt; Find drivers or fleet partners, agree on earnings and explain why they should join a new service with few customers.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Verification.&lt;/strong&gt; Check identities, licences and vehicle documents; arrange appropriate background checks with consent. Keep track of documents that need renewing.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Vehicle checks.&lt;/strong&gt; Assess condition, maintenance and suitability. You need to know which vehicles are ready for work, not just how many are registered.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Onboarding.&lt;/strong&gt; Teach drivers how requests, pickups, cancellations and support work. Agree on the service customers should expect.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Daily coverage.&lt;/strong&gt; Plan for busy hours, drivers going offline and vehicles breaking down. Someone has to deal with the gaps.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Local access.&lt;/strong&gt; Build relationships around estates, campuses, terminals and pickup points. Work out the operating requirements for your service.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Support and payments.&lt;/strong&gt; Handle missed pickups, disputes, incidents and settlements. Follow up on recurring complaints.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Then you want to expand. A launch in another state means finding reliable local partners and working through much of that list again. Meanwhile, customers are still asking for improvements to the app.&lt;/p&gt;

&lt;p&gt;For a solo developer, this is a serious constraint on how much you can build and how far you can reach.&lt;/p&gt;

&lt;h2&gt;
  
  
  Connect the booking flow to Relay
&lt;/h2&gt;

&lt;p&gt;With Relay, your backend sends the pickup, destination and task requirements through an API. Your app can use task events and location updates to show what is happening: waiting for a driver, assigned, on the way or completed.&lt;/p&gt;

&lt;p&gt;That leaves more time for the details customers notice. Can they place the pickup pin outside the right gate? Can they book without calling for help? Is it clear what happens when no driver is available?&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fzckqqmaguyxjddqpgoq7.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fzckqqmaguyxjddqpgoq7.png" alt="Example booking screen with an Ikoyi street map, vehicle choices and simulated car locations." width="800" height="550"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;An example app screen with simulated driver locations.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;You can test that experience before arranging a live launch. Relay's test mode gives you simulated drivers, so you can work through the booking and tracking flow without assembling a fleet just to try your idea.&lt;/p&gt;

&lt;p&gt;You still run your customer-facing business. Support, pricing and the responsibilities of a passenger service remain yours to organise. Before launch, agree with Relay on driver supply, verification standards and the operating arrangements for your service. Relay currently describes its coverage as a Lagos and Abuja pilot; confirm your launch area and vehicle requirements with the team.&lt;/p&gt;

&lt;h2&gt;
  
  
  Cars, kekes or motorcycles
&lt;/h2&gt;

&lt;p&gt;Your product does not have to look like every other booking app. You might want a car service for a particular community, a keke booking app for short local journeys, or a motorcycle service where passenger operations are permitted.&lt;/p&gt;

&lt;p&gt;Relay lets your backend specify the vehicle category for a task:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Your app&lt;/th&gt;
&lt;th&gt;API vehicle preference&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Car bookings&lt;/td&gt;
&lt;td&gt;&lt;code&gt;CAR&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Keke bookings&lt;/td&gt;
&lt;td&gt;&lt;code&gt;TRICYCLE&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Motorcycle bookings&lt;/td&gt;
&lt;td&gt;&lt;code&gt;MOTORCYCLE&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;For an SUV service, discuss the required vehicles with Relay. There is no separate &lt;code&gt;SUV&lt;/code&gt; category in the current API, so selecting &lt;code&gt;CAR&lt;/code&gt; alone would not guarantee one. Each service also needs suitable supply in its launch area.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fppdkhed4k7nvwbafxhb8.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fppdkhed4k7nvwbafxhb8.png" alt="Car, SUV, keke and motorcycle models." width="800" height="533"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you want to work with particular fleet partners, you can specify &lt;code&gt;allowedOrganisations&lt;/code&gt; and set &lt;code&gt;allowIndependentRiders: false&lt;/code&gt;. Your app can then match within that selected group. The &lt;a href="https://docs.sendrelay.com.ng/tasks/create-task" rel="noopener noreferrer"&gt;matching documentation&lt;/a&gt; covers these controls.&lt;/p&gt;

&lt;p&gt;This makes it easier to try different products through the same integration, without personally starting another recruitment operation for each one.&lt;/p&gt;

&lt;h2&gt;
  
  
  Try a booking from your backend
&lt;/h2&gt;

&lt;p&gt;Here is a Node.js request for a simulated passenger task. Use a Relay test key; this example does not book a real driver.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;RelayClient&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@relay-sdk/sdk-node&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;apiKey&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;RELAY_API_KEY&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;apiKey&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nf"&gt;startsWith&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;sk_test_&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Use a Relay test key for this example&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;relay&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;RelayClient&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;apiKey&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;task&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;relay&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;tasks&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;taskType&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;PASSENGER_RIDE&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;preferences&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;vehicleType&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;CAR&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;stages&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;PICKUP&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;instructions&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Simulation only&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;location&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;latitude&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;6.4541&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;longitude&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;3.4246&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;address&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Demo pickup, Ikoyi, Lagos&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="na"&gt;passengers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Demo passenger&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;luggageCount&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="p"&gt;}],&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;DROPOFF&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;instructions&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Simulation only&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;location&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;latitude&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;6.4474&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;longitude&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;3.4495&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;address&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Demo destination, Ikoyi, Lagos&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="na"&gt;autoAssign&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;paymentMethod&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;WALLET&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;simulationOutcome&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;SUCCESS&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;idempotencyKey&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;ride-demo:booking-001:attempt-1&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;task&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;task&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Keep the API key on your server and save the returned task ID against the booking. Reuse the same request and idempotency key when retrying that booking attempt; use a new key for a new booking.&lt;/p&gt;

&lt;p&gt;Next, connect verified webhooks to your backend and a task-scoped WebSocket subscription to your UI. Try cancellations and the no-driver outcome as well as a successful booking. If you show a price before dispatch, &lt;a href="https://docs.sendrelay.com.ng/getting-started/quotes-and-delayed-commit" rel="noopener noreferrer"&gt;request a quote first&lt;/a&gt; and create the task before it expires.&lt;/p&gt;

&lt;p&gt;You now have something you can build a product around. Relay also has &lt;a href="https://sendrelay.com.ng/for-developers" rel="noopener noreferrer"&gt;browser and Flutter tooling&lt;/a&gt; for the customer-facing part of the app.&lt;/p&gt;

&lt;h2&gt;
  
  
  What could you build beyond the first city?
&lt;/h2&gt;

&lt;p&gt;Access to a shared network changes the expansion question. As Relay adds drivers and supported areas, you can explore those markets through the integration you already have, instead of beginning every launch by finding and onboarding another fleet yourself.&lt;/p&gt;

&lt;p&gt;There are opportunities beyond passenger bookings, too. Within a state, a retailer could arrange pickups from several shops and deliveries to customers through one app. Across states, a business could let sellers request transport to customers or collection points along supported routes. Both depend on suitable vehicles and confirmed route coverage, but the developer can build the ordering, booking and tracking experience around the same task API.&lt;/p&gt;

&lt;p&gt;That is the appeal of adopting Relay early: you can start with a focused local product and expand what it does as the network grows. E-hailing is the example here. We will cover other apps you can build with Relay in upcoming posts.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://sendrelay.com.ng/?utm_source=devto&amp;amp;utm_medium=social&amp;amp;utm_campaign=build_the_app" rel="noopener noreferrer"&gt;Get a test key and explore Relay&lt;/a&gt; · &lt;a href="https://docs.sendrelay.com.ng/?utm_source=devto&amp;amp;utm_medium=social&amp;amp;utm_campaign=build_the_app" rel="noopener noreferrer"&gt;Read the API docs&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;Map: &lt;a href="https://openfreemap.org/" rel="noopener noreferrer"&gt;OpenFreeMap&lt;/a&gt; / &lt;a href="https://openmaptiles.org/" rel="noopener noreferrer"&gt;OpenMapTiles&lt;/a&gt;, © &lt;a href="https://www.openstreetmap.org/copyright" rel="noopener noreferrer"&gt;OpenStreetMap contributors&lt;/a&gt; (ODbL).&lt;/p&gt;

</description>
      <category>api</category>
      <category>webdev</category>
      <category>nigeria</category>
      <category>startup</category>
    </item>
    <item>
      <title>No rider accepted your delivery task. What should your app do?</title>
      <dc:creator>SendRelay</dc:creator>
      <pubDate>Mon, 19 Oct 2026 15:46:33 +0000</pubDate>
      <link>https://dev.to/sendrelay-ng/no-rider-accepted-your-delivery-task-what-should-your-app-do-2l82</link>
      <guid>https://dev.to/sendrelay-ng/no-rider-accepted-your-delivery-task-what-should-your-app-do-2l82</guid>
      <description>&lt;p&gt;An order is ready at a pharmacy in Wuse 2, Abuja. The customer opens your app to check on delivery. Your backend has created a Relay task, but no rider accepts the offer. If your screen still says “finding a rider,” the customer has no idea what is happening, and your operations team has no clear next step.&lt;/p&gt;

&lt;h2&gt;
  
  
  What you'll build
&lt;/h2&gt;

&lt;p&gt;You'll handle the no-rider branch: store the task ID, observe its terminal state, and show a customer message that your app can actually support. Start by rehearsing it with a test key:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;RelayClient&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@relay-sdk/sdk-node&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;relay&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;RelayClient&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;apiKey&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;RELAY_API_KEY&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt; &lt;span class="c1"&gt;// sk_test_... only&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;task&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;relay&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;tasks&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;taskType&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;PACKAGE_DELIVERY&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;autoAssign&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;simulationOutcome&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;NO_RIDERS&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;stages&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;PICKUP&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;location&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;latitude&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;9.0579&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;longitude&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;7.4951&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;address&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Demo pickup, Wuse 2, Abuja&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="na"&gt;instructions&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Simulation only&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;items&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Demo parcel&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;estimatedValue&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;100000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;estimatedWeight&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;LIGHT&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;estimatedSize&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;BOX&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;}],&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;DROPOFF&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;location&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;latitude&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;9.0765&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;longitude&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;7.3986&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;address&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Demo dropoff, Abuja&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="na"&gt;instructions&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Simulation only&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;items&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[],&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;],&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="c1"&gt;// Persist task.id with your order. Later, reconcile from Relay:&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;latest&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;relay&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;tasks&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;task&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;latest&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;task&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;latest&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;task&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;failureReason&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The addresses and coordinates are demonstration inputs. &lt;code&gt;estimatedValue&lt;/code&gt; is the parcel's value in &lt;strong&gt;kobo&lt;/strong&gt;, not the delivery fee. &lt;code&gt;NO_RIDERS&lt;/code&gt; works with &lt;code&gt;sk_test_...&lt;/code&gt; keys; test mode simulates the lifecycle and does not dispatch a real rider or move real money. Follow the &lt;a href="https://docs.sendrelay.com.ng/sdks/node-sdk/" rel="noopener noreferrer"&gt;Node SDK guide&lt;/a&gt; and &lt;a href="https://docs.sendrelay.com.ng/simulation/outcomes/" rel="noopener noreferrer"&gt;simulation outcomes&lt;/a&gt; for setup.&lt;/p&gt;

&lt;h2&gt;
  
  
  What happens when nobody accepts?
&lt;/h2&gt;

&lt;p&gt;In the live auto-assignment path, Relay looks for eligible nearby riders in configured offer tiers. A tier can find no eligible riders, receive no deliverable offers, or reach its decision deadline without an accepted assignment. The workflow checks for an assignment before ending the search. If none remains, it marks the task &lt;code&gt;FAILED&lt;/code&gt; with reason &lt;code&gt;NO_RIDERS_AVAILABLE&lt;/code&gt; and emits a task-failed event.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fg88tub2fbimwsydu7hb7.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fg88tub2fbimwsydu7hb7.png" alt="Three empty offer trays and silent bells sit beside a parcel under a frosted dome; a loose green thread remains unattached." width="800" height="800"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;That distinction matters in your app. &lt;code&gt;OFFERED&lt;/code&gt; is still in progress; &lt;code&gt;FAILED&lt;/code&gt; is terminal. Don't promise a rider while an offer is pending, and don't keep showing a spinner after the failure. A webhook can synchronize your backend; &lt;code&gt;GET /tasks/{taskId}&lt;/code&gt; lets you reconcile state when a webhook is delayed or your service restarts. Relay also supports WebSocket updates for a connected UI. See the &lt;a href="https://docs.sendrelay.com.ng/tasks/create-task/" rel="noopener noreferrer"&gt;task lifecycle&lt;/a&gt; and &lt;a href="https://docs.sendrelay.com.ng/webhooks/" rel="noopener noreferrer"&gt;webhook guide&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Give the customer and your team a useful next step
&lt;/h2&gt;

&lt;p&gt;Back in Wuse 2, your customer should see a plain status such as: “We couldn't find a rider for this delivery. We're checking another option.” That wording is &lt;strong&gt;your product choice&lt;/strong&gt;, not an automatic Relay message. Your operations view should show the failed task and its reason so a person can decide whether to adjust the order, contact the customer, or create a new task. Do not silently treat the failed task as delivered.&lt;/p&gt;

&lt;p&gt;If you retry from your backend, use the documented task-creation idempotency behavior to avoid duplicate task creation from network retries. A new dispatch attempt is a product decision: review the current task state and your payment flow first. Relay's live failure path refunds a wallet-funded task and attempts to void a card preauthorization before publishing failure; your UI should still use the task and payment records as its source of truth rather than promising an instant bank refund.&lt;/p&gt;

&lt;h2&gt;
  
  
  Rehearse the failure branch
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;a href="https://docs.sendrelay.com.ng/getting-started/get-api-key/" rel="noopener noreferrer"&gt;Get a test API key&lt;/a&gt; and create a task with &lt;code&gt;simulationOutcome: 'NO_RIDERS'&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Save the returned &lt;code&gt;task.id&lt;/code&gt; against your order.&lt;/li&gt;
&lt;li&gt;Listen for the task-failed webhook and verify its signature; keep the handler idempotent.&lt;/li&gt;
&lt;li&gt;Retrieve the task by ID to reconcile the final status. Check &lt;code&gt;FAILED&lt;/code&gt; and the failure reason before updating your order and customer UI.&lt;/li&gt;
&lt;li&gt;Repeat with &lt;code&gt;TIER_3_TIMEOUT&lt;/code&gt; to exercise the full timeout path. The &lt;a href="https://docs.sendrelay.com.ng/simulation/outcomes/" rel="noopener noreferrer"&gt;simulation matrix&lt;/a&gt; lists the expected outcomes.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;When your test task fails, check the customer screen and operations view together. Both should reflect the same failed task before anyone decides what to do with the order. &lt;a href="https://sendrelay.com.ng?utm_source=devto&amp;amp;utm_medium=social&amp;amp;utm_campaign=no-rider-accepted" rel="noopener noreferrer"&gt;Create a Relay account and get API keys&lt;/a&gt;, then use the &lt;a href="https://docs.sendrelay.com.ng/tasks/create-task/" rel="noopener noreferrer"&gt;task creation guide&lt;/a&gt; to run the scenario.&lt;/p&gt;

</description>
      <category>api</category>
      <category>node</category>
      <category>logistics</category>
      <category>nigeria</category>
    </item>
    <item>
      <title>Create a delivery once, even when your Node worker retries</title>
      <dc:creator>SendRelay</dc:creator>
      <pubDate>Wed, 30 Sep 2026 18:42:07 +0000</pubDate>
      <link>https://dev.to/sendrelay-ng/create-a-delivery-once-even-when-your-node-worker-retries-4mlb</link>
      <guid>https://dev.to/sendrelay-ng/create-a-delivery-once-even-when-your-node-worker-retries-4mlb</guid>
      <description>&lt;p&gt;You run an online pharmacy in Wuse 2, Abuja. A customer confirms an order, and your checkout worker starts a delivery. The request times out just as the worker loses its connection. Did the task get created? Retrying with a fresh request can leave your team looking at two dispatches for one order.&lt;/p&gt;

&lt;p&gt;Relay lets your backend attach an idempotency key to task creation. Keep that key tied to the order's delivery intent, and a retry can recover the existing task. The customer sees one delivery; your ops team has one task to track. Here is how to wire that into a Node worker.&lt;/p&gt;

&lt;h2&gt;
  
  
  What you'll build
&lt;/h2&gt;

&lt;p&gt;A Node function that creates a package delivery for an Abuja order and safely retries an uncertain result with the same key. You'll use the &lt;a href="https://docs.sendrelay.com.ng/sdks/node-sdk" rel="noopener noreferrer"&gt;Node SDK&lt;/a&gt; and the &lt;a href="https://docs.sendrelay.com.ng/tasks/create-task" rel="noopener noreferrer"&gt;task creation guide&lt;/a&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;RelayClient&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@relay-sdk/sdk-node&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;apiKey&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;RELAY_API_KEY&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;apiKey&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;RELAY_API_KEY is required&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;relay&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;RelayClient&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;apiKey&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;Order&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;createOrderDelivery&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;order&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Order&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// Persist the order ID before dispatching this job. Reuse it on every retry.&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;idempotencyKey&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`order:&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;order&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;:delivery:v1`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;relay&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;tasks&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;taskType&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;PACKAGE_DELIVERY&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;externalId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;order&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;stages&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
      &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;PICKUP&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;location&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;latitude&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;9.0765&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;longitude&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;7.3986&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;address&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Wuse 2, Abuja&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="na"&gt;instructions&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Ask for the pharmacy desk&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;items&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt;
          &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Packed order&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="na"&gt;estimatedValue&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;500000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="na"&gt;estimatedWeight&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;LIGHT&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="na"&gt;estimatedSize&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;BOX&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;}],&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;DROPOFF&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;location&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;latitude&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;9.0579&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;longitude&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;7.4951&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;address&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Garki, Abuja&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="na"&gt;instructions&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Call the recipient on arrival&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;idempotencyKey&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="c1"&gt;// Store this ID against your order so later events can update the right row.&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;taskId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;task&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;duplicate&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;idempotency&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;duplicate&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Install the package with &lt;code&gt;npm install @relay-sdk/sdk-node&lt;/code&gt;. Keep &lt;code&gt;RELAY_API_KEY&lt;/code&gt; on your server. The SDK sends the explicit key as an &lt;code&gt;Idempotency-Key&lt;/code&gt; header. Its automatically generated POST keys help within a request, but a stable order-based key is what makes a later job retry refer to the same delivery.&lt;/p&gt;

&lt;h2&gt;
  
  
  Retry the same intent, not a new delivery
&lt;/h2&gt;

&lt;p&gt;If the worker times out or its queue redelivers the job, call &lt;code&gt;createOrderDelivery(order)&lt;/code&gt; again with the same persisted order ID and the same delivery details. Do not add a timestamp or random value to the key. Relay looks up a task by account and key; a replay returns the existing task with &lt;code&gt;idempotency.duplicate: true&lt;/code&gt;. The key is for one delivery intent. If the customer requests a genuinely new delivery, use a new versioned key.&lt;/p&gt;

&lt;p&gt;The API describes a 24-hour validity window for this retry flow. Keep your own order-to-task record as the longer-lived source of truth. A timeout does not prove creation failed: retry with the same key, then record the returned task ID. If the API keeps failing, let your queue retry later with that same key rather than issuing an unkeyed create.&lt;/p&gt;

&lt;h2&gt;
  
  
  Try it without dispatching a rider
&lt;/h2&gt;

&lt;p&gt;Start with a &lt;code&gt;sk_test_...&lt;/code&gt; key from your &lt;a href="https://dashboard.sendrelay.com.ng/signup?utm_source=devto&amp;amp;utm_medium=social&amp;amp;utm_campaign=retry-safe-delivery-creation" rel="noopener noreferrer"&gt;Relay dashboard&lt;/a&gt;. Test-key task creation runs a simulation: it does not assign a real rider or move real money. The server selects the internal &lt;code&gt;TEST&lt;/code&gt; payment path, so do not send &lt;code&gt;paymentMethod: 'TEST'&lt;/code&gt;. You can pass &lt;code&gt;simulationOutcome: 'SUCCESS'&lt;/code&gt; in the create body while checking the flow. Create the same order twice and check that the second result reports &lt;code&gt;duplicate: true&lt;/code&gt; and the same task ID.&lt;/p&gt;

&lt;p&gt;For the response shape, lifecycle, and test outcomes, see &lt;a href="https://docs.sendrelay.com.ng/tasks/create-task" rel="noopener noreferrer"&gt;Create Task&lt;/a&gt; and &lt;a href="https://docs.sendrelay.com.ng/simulation/outcomes" rel="noopener noreferrer"&gt;Simulation Outcomes&lt;/a&gt;. For your checkout, &lt;a href="https://dashboard.sendrelay.com.ng/signup?utm_source=devto&amp;amp;utm_medium=social&amp;amp;utm_campaign=retry-safe-delivery-creation" rel="noopener noreferrer"&gt;get your API keys&lt;/a&gt; and keep the key generation on your backend.&lt;/p&gt;

</description>
      <category>node</category>
      <category>api</category>
      <category>logistics</category>
      <category>nigeria</category>
    </item>
    <item>
      <title>The first two minutes of a delivery (and everything after)</title>
      <dc:creator>SendRelay</dc:creator>
      <pubDate>Wed, 30 Sep 2026 18:37:57 +0000</pubDate>
      <link>https://dev.to/sendrelay-ng/the-first-two-minutes-of-a-delivery-and-everything-after-5eg4</link>
      <guid>https://dev.to/sendrelay-ng/the-first-two-minutes-of-a-delivery-and-everything-after-5eg4</guid>
      <description>&lt;p&gt;In our &lt;a href="https://dev.to/sendrelay-ng/where-is-my-cake-a-saturday-in-lagos-told-twice-h02"&gt;first post&lt;/a&gt; a cake got across Lagos without anyone making a phone call. In the &lt;a href="https://dev.to/sendrelay-ng/what-would-you-build-if-delivery-were-the-easy-part-10gd"&gt;second&lt;/a&gt;, we looked at the businesses that open up when delivery stops being the hard part.&lt;/p&gt;

&lt;p&gt;People keep asking the same follow-up question: what actually happens in between? You press a button, a rider turns up. What goes on in the middle?&lt;/p&gt;

&lt;p&gt;So let's slow one delivery right down and watch it.&lt;/p&gt;

&lt;h2&gt;
  
  
  10:02 a.m. The order is ready
&lt;/h2&gt;

&lt;p&gt;A pharmacy in Wuse 2, Abuja. A customer in Maitama has ordered a repeat prescription, including insulin, which has to stay cold. The pharmacist has done her checks, packed the order, and taps "ready".&lt;/p&gt;

&lt;p&gt;The price was settled earlier, when the customer checked out. Relay gave the pharmacy's app a quote, and that price held while the order was being prepared. Nobody haggles at the door, and nobody gets a surprise at the end.&lt;/p&gt;

&lt;p&gt;The moment the order is marked ready, it becomes a delivery on Relay, waiting for a rider. The clock starts.&lt;/p&gt;

&lt;h2&gt;
  
  
  10:02 and one second. Who could take this?
&lt;/h2&gt;

&lt;p&gt;Relay looks around the pharmacy for riders who could do this particular job well. Not just anyone nearby: riders who are online right now, who are rated highly enough, whose vehicle suits the trip, and, because of the insulin, who can keep things cold.&lt;/p&gt;

&lt;p&gt;That last part matters. The pharmacy asked for refrigeration when it created the delivery, so a rider without it is never offered the job. It isn't a note someone might forget to read. The rider simply doesn't qualify.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fmka65hxvnfhp4rdapfku.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fmka65hxvnfhp4rdapfku.png" alt="Three waves of offers spreading out from the pharmacy: first the best-placed rider, then up to three riders, then up to twenty" width="800" height="500"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The first two minutes: three waves
&lt;/h2&gt;

&lt;p&gt;Here's the part most people never see. Relay doesn't shout the job to every rider in the city at once. It asks in waves.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The first wave goes to one rider&lt;/strong&gt;, the best-placed one, who has thirty seconds to accept. One rider, one clear offer, no scramble.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;If they don't take it, the second wave goes to up to three riders&lt;/strong&gt;, for another thirty seconds. Whoever accepts first gets the job.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;If it's still open, the third wave widens to up to twenty riders&lt;/strong&gt;, for a final minute.&lt;/p&gt;

&lt;p&gt;Those are the default timings. An urgent delivery moves faster, with shorter windows at every wave. Either way, within about two minutes you know where you stand.&lt;/p&gt;

&lt;p&gt;And if nobody accepts? That can happen, especially late at night or in a heavy rainstorm. The delivery is marked as failed, the pharmacy is told straight away, and if the customer paid by card, the amount that was held on it is let go. Nobody's card is charged for a delivery that didn't happen, and the pharmacy isn't left waiting on a promise.&lt;/p&gt;

&lt;h2&gt;
  
  
  10:03 a.m. Someone says yes
&lt;/h2&gt;

&lt;p&gt;A rider called Musa accepts from a few streets away. The delivery is now assigned to him.&lt;/p&gt;

&lt;p&gt;On the customer's phone, the order page changes. It no longer says "preparing". It says a rider is on the way to the pharmacy, and shows him moving on the map.&lt;/p&gt;

&lt;p&gt;This is the moment that used to be a string of phone calls. Now it happens without anyone picking up a phone.&lt;/p&gt;

&lt;h2&gt;
  
  
  10:09 a.m. The pickup
&lt;/h2&gt;

&lt;p&gt;Musa arrives and collects the package. By now the delivery is "in progress", and the pickup is recorded as done, with the time.&lt;/p&gt;

&lt;p&gt;If a delivery has several stops, each one is recorded the same way as it's completed. That's what lets the aso-ebi coordinator from our last post see exactly which of her forty guests have their outfits and which are still waiting.&lt;/p&gt;

&lt;h2&gt;
  
  
  10:10 to 10:24 a.m. The part everyone can see
&lt;/h2&gt;

&lt;p&gt;Musa rides towards Maitama. His position updates live, so the customer can watch him come, and the pharmacy can see him from the counter without asking anyone.&lt;/p&gt;

&lt;p&gt;If the customer steps out to buy bread, they can check their phone and know whether to hurry back. If traffic on the way is slow, everyone can see that too. Nobody has to guess, and nobody has to call.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F6dvq5wk39cge2sidc5qc.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F6dvq5wk39cge2sidc5qc.png" alt="The life of one delivery: pending, offered, assigned, in progress and completed, with the payment held along the way and released at the end" width="800" height="420"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  10:25 a.m. Delivered
&lt;/h2&gt;

&lt;p&gt;The insulin arrives cold. The delivery is marked complete.&lt;/p&gt;

&lt;p&gt;A few quiet things happen at this moment, and they're the parts businesses end up valuing most.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;A card payment is collected now, not before.&lt;/strong&gt; If the customer paid by card, the amount was only held when the delivery was created. It's charged once the job is actually done.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Musa's earnings are recorded straight away, then held for a short while before they're released.&lt;/strong&gt; That short wait leaves room to raise a problem if something went wrong, before the money has gone anywhere.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The pharmacy can rate the rider.&lt;/strong&gt; Ratings are part of how riders are matched, so the next business that asks for well-rated riders benefits from this one's feedback.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;And everything is written down.&lt;/strong&gt; When the order was ready, when it was offered, who accepted, when the pickup happened, when it arrived, what it cost, and when everyone was paid. If a customer calls next week to ask about their order, the answer is a few seconds away.&lt;/p&gt;

&lt;h2&gt;
  
  
  What didn't happen
&lt;/h2&gt;

&lt;p&gt;It's worth noticing what nobody had to do.&lt;/p&gt;

&lt;p&gt;Nobody rang round for a rider. Nobody negotiated a price at the door. Nobody answered "where is it?" three times. Nobody paid cash and tried to remember it at the end of the month. And the pharmacist never stopped being a pharmacist to become a dispatcher for half an hour.&lt;/p&gt;

&lt;p&gt;That's the whole idea. The delivery still happens in the real world, on real roads, with a real person on a real bike. Relay just takes the uncertainty out of the middle.&lt;/p&gt;

&lt;h2&gt;
  
  
  For the developers reading
&lt;/h2&gt;

&lt;p&gt;Everything above maps to a small, predictable set of states and events, so your app can follow along exactly.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A delivery moves through &lt;strong&gt;pending → offered → assigned → in progress → completed&lt;/strong&gt;. It can also end as &lt;strong&gt;failed&lt;/strong&gt; (no rider accepted) or &lt;strong&gt;cancelled&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Each change arrives as a webhook your server can listen for: &lt;code&gt;task.status.created&lt;/code&gt;, &lt;code&gt;task.status.offered&lt;/code&gt;, &lt;code&gt;task.status.assigned&lt;/code&gt;, &lt;code&gt;task.status.in_progress&lt;/code&gt;, &lt;code&gt;task.status.completed&lt;/code&gt;, and &lt;code&gt;task.status.failed&lt;/code&gt; or &lt;code&gt;task.status.cancelled&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Every finished stop sends &lt;code&gt;task.stage.completed&lt;/code&gt;, which is handy for multi-stop runs.&lt;/li&gt;
&lt;li&gt;The money has its own events: &lt;code&gt;payment.pending&lt;/code&gt;, &lt;code&gt;payment.released&lt;/code&gt;, &lt;code&gt;payment.disputed&lt;/code&gt;, and &lt;code&gt;payment.dispute_resolved&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Live location for maps comes over a WebSocket, so your customer's screen updates without polling.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The part we like most is that you can rehearse all of it before going live. With a test key, you can tell Relay how a delivery should turn out, whether that's a smooth success, a first wave that times out, no riders at all, or a cancellation after the rider accepts, and check that your app handles each one gracefully. The &lt;a href="https://docs.sendrelay.com.ng/simulation/outcomes" rel="noopener noreferrer"&gt;simulation guide&lt;/a&gt; lists every outcome, and our &lt;a href="https://dev.to/sendrelay-ng/create-your-first-delivery-task-with-relay-node-test-mode-34jm"&gt;Node walkthrough&lt;/a&gt; gets you to your first test delivery.&lt;/p&gt;

&lt;h2&gt;
  
  
  Two minutes, and then some
&lt;/h2&gt;

&lt;p&gt;Most of what makes a delivery feel good to a customer happens in the first two minutes and the last one: someone reliable says yes quickly, and the thing arrives the way it left. Everything in between should be visible, fair, and written down.&lt;/p&gt;

&lt;p&gt;That's the job we built Relay to do, so the pharmacist can stay a pharmacist.&lt;/p&gt;

&lt;p&gt;If you'd like Relay behind your deliveries in Lagos or Abuja, start at &lt;a href="https://sendrelay.com.ng/?utm_source=devto&amp;amp;utm_medium=social&amp;amp;utm_campaign=devto_first_two_minutes" rel="noopener noreferrer"&gt;sendrelay.com.ng&lt;/a&gt; or write to &lt;a href="mailto:hello@sendrelay.com.ng"&gt;hello@sendrelay.com.ng&lt;/a&gt;.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Illustrations by Relay. The pharmacy, the customer and Musa are an illustrative example, not a real delivery.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>nigeria</category>
      <category>startup</category>
      <category>api</category>
      <category>logistics</category>
    </item>
    <item>
      <title>What would you build if delivery were the easy part?</title>
      <dc:creator>SendRelay</dc:creator>
      <pubDate>Mon, 28 Sep 2026 15:34:28 +0000</pubDate>
      <link>https://dev.to/sendrelay-ng/what-would-you-build-if-delivery-were-the-easy-part-10gd</link>
      <guid>https://dev.to/sendrelay-ng/what-would-you-build-if-delivery-were-the-easy-part-10gd</guid>
      <description>&lt;p&gt;In &lt;a href="https://dev.to/sendrelay-ng/where-is-my-cake-a-saturday-in-lagos-told-twice-h02"&gt;our first post&lt;/a&gt;, we followed one cake from Yaba to Lekki. This time, let's zoom out.&lt;/p&gt;

&lt;p&gt;Look closely at almost any business in Lagos or Abuja and you'll find a small delivery company hiding inside it. The pharmacy has a boy on a bike. The tailor has a cousin who "knows a rider". The caterer borrows her brother's car on Saturdays. None of them set out to run logistics, but every one of them does, a little badly, every day.&lt;/p&gt;

&lt;p&gt;Relay exists so they don't have to. And once moving things across a city stops being the hard part, a lot of ideas that used to be too much trouble start to look like good businesses.&lt;/p&gt;

&lt;p&gt;Here are some of them. A few are obvious. Most, we hope, are not.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fs5lijznbwbhsudci97js.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fs5lijznbwbhsudci97js.png" alt="Five task patterns: across town, ordered dropoffs, ordered pickups, separate outward and return tasks, and passenger rides." width="800" height="625"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Multi-stop tasks follow the stages in the order you provide. A return journey uses two tasks.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  First, the ones you'd expect
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Restaurants and cloud kitchens.&lt;/strong&gt; The order is packed, a nearby rider is offered the job, and your customer watches the food come to them instead of calling to ask.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Pharmacies.&lt;/strong&gt; You can request riders whose vehicle profile records refrigeration. Your business still needs to verify the equipment and handling required for the medicine.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Online shops.&lt;/strong&gt; Show a quote at checkout, then create the same task with its quote ID before it expires. Relay uses that locked amount for the task.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Groceries and errands.&lt;/strong&gt; Someone else does the market run. You follow it from your phone.&lt;/p&gt;

&lt;p&gt;These are the reasons most people come to Relay. The interesting part is what else becomes possible.&lt;/p&gt;

&lt;h2&gt;
  
  
  Across town, right now
&lt;/h2&gt;

&lt;p&gt;Some deliveries are small and urgent. They don't need a van; they need someone reliable, quickly.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The contract that must be signed today.&lt;/strong&gt; A law firm in Ikoyi needs a signed agreement at a bank in Victoria Island before three o'clock. You can set an urgent priority and a minimum rider rating, or manually offer a pending task to selected available riders. Your app can show the envelope's progress with tracking events; the deadline is the firm's aim, not a delivery guarantee.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The part that gets the car moving again.&lt;/strong&gt; A driver breaks down on the Lekki-Epe Expressway. The mechanic knows exactly which part he needs, and it's sitting in a shop at Ladipo. An app that connects roadside mechanics to parts dealers, and sends the part out the moment it's paid for, is a real business. It only ever needed a delivery network that nobody could afford to build alone.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The thing you forgot at home.&lt;/strong&gt; Your laptop charger. Your ID card. The keys your sister needs. A simple "send anything across town" app, built for the everyday version of this problem, can use Relay for task creation and tracking while you build the rest of the app.&lt;/p&gt;

&lt;h2&gt;
  
  
  One pickup, many doors
&lt;/h2&gt;

&lt;p&gt;A single Relay delivery can have several stops. The task follows the stages in the order you provide.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Aso-ebi season.&lt;/strong&gt; One tailor, forty guests, forty outfits, all due before the wedding on Saturday. Instead of forty phone calls and a very long week, it's one pickup and a list of drop-offs, and your app can use task location and stage events to show guests progress. Anyone who has ever coordinated aso-ebi for a family wedding is already smiling.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Subscriptions that go out on a round.&lt;/strong&gt; Weekly zobo and smoothies. Meal prep for busy professionals. Fresh bread every Friday morning. A small subscription business lives or dies on getting the same thing to the same thirty doors, reliably, every week.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Giving, delivered.&lt;/strong&gt; Churches, mosques, and community groups that send food packs to families can plan a run of drop-offs and know that every stop was reached, with a record to show for it.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fimages.unsplash.com%2Fphoto-1785094165709-54fef69c2c4d%3Fw%3D1200%26h%3D675%26fit%3Dcrop%26crop%3Dentropy%26fm%3Djpg%26q%3D80" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fimages.unsplash.com%2Fphoto-1785094165709-54fef69c2c4d%3Fw%3D1200%26h%3D675%26fit%3Dcrop%26crop%3Dentropy%26fm%3Djpg%26q%3D80" alt="A busy Lagos market seen from above, with umbrellas, stalls, cars and shoppers" width="1200" height="675"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Many pickups, one door
&lt;/h2&gt;

&lt;p&gt;Flip it around. Several stops to collect, one place to deliver.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;A personal shopper for Balogun.&lt;/strong&gt; The fabric from one stall, the lace from another, the shoes from a third. A shopper walks the market, and one rider collects from every stall and brings it all to one customer. For a customer elsewhere in Lagos, or just someone who can't face the market, that's a service worth paying for.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The morning ingredient run.&lt;/strong&gt; A restaurant buying fresh produce from Mile 12 every morning. Peppers from one seller, onions from another, everything to the kitchen before lunch service.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Event day.&lt;/strong&gt; The cake from Yaba, the flowers from Obalende, the small chops from Surulere, all arriving at the venue before the guests do.&lt;/p&gt;

&lt;h2&gt;
  
  
  There and back
&lt;/h2&gt;

&lt;p&gt;Some ideas only work if the thing comes back. Those used to be twice as hard. With Relay they're two deliveries: one out, one home. Your app creates the return task when the item is ready; each task has its own quote, tracking and payment.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Laundry and dry cleaning&lt;/strong&gt; that collects on Monday and returns on Thursday, without the business ever buying a van.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Phone and laptop repairs.&lt;/strong&gt; The cracked phone goes to the technician; the fixed phone comes back. The customer never has to sit in traffic to Computer Village.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Renting instead of buying.&lt;/strong&gt; Cameras for a weekend shoot. A PlayStation for the holidays. A dress for one wedding. Baby gear that's only needed for a few months. Rental businesses are held back by one question: how does it get there, and how does it come back? Answer that, and a whole category of small businesses opens up.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fimages.unsplash.com%2Fphoto-1529171918672-ba6d0733a56c%3Fw%3D1200%26h%3D675%26fit%3Dcrop%26crop%3Dentropy%26fm%3Djpg%26q%3D80" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fimages.unsplash.com%2Fphoto-1529171918672-ba6d0733a56c%3Fw%3D1200%26h%3D675%26fit%3Dcrop%26crop%3Dentropy%26fm%3Djpg%26q%3D80" alt="The view from inside a keke, looking out at motorcycles, cars and pedestrians on a busy road" width="1200" height="675"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Choose vehicle and equipment filters
&lt;/h2&gt;

&lt;p&gt;When you create a task, you can filter by vehicle type and by box, air-conditioning or refrigeration flags recorded on rider vehicle profiles. These filters do not verify temperature, packaging quality or suitability for sensitive goods.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Refrigeration requirements.&lt;/strong&gt; For goods that need refrigeration, request the refrigeration flag and verify the available equipment and handling with your delivery operation.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Fragile and beautiful things.&lt;/strong&gt; For cakes, flowers or artwork, you can request a box or air-conditioning flag. Confirm packaging and handling separately.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Heavy things.&lt;/strong&gt; For a proposed bulk-delivery integration, choose a bulk-delivery task and filter for a truck. Verify suitable vehicle capacity and handling with your operation; the API does not guarantee truck supply.&lt;/p&gt;

&lt;h2&gt;
  
  
  Passenger rides
&lt;/h2&gt;

&lt;p&gt;Relay supports passenger-ride tasks.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The night shift.&lt;/strong&gt; A hospital, restaurant or hotel can build a staff ride service. Your app can create passenger tasks and use tracking events to show progress. Driver suitability and vehicle availability still need to be checked.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Guests.&lt;/strong&gt; A hotel can build a guest pickup flow using passenger tasks and tracking events. Suitable vehicles, driver checks and any airport permissions remain the operation's responsibility.&lt;/p&gt;

&lt;h2&gt;
  
  
  Bring your own riders
&lt;/h2&gt;

&lt;p&gt;Maybe you already have riders.&lt;/p&gt;

&lt;p&gt;For manual dispatch, create a live task with autoAssign set to false. Retrieve available riders and offer the pending task to selected rider IDs. A rider must be eligible and accept the offer. The repo also includes an MIT-licensed Flutter location engine for integrating tracking into a rider app.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fwux8sow4b7qtg2fhufu8.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fwux8sow4b7qtg2fhufu8.png" alt="Six task controls: rider matching, equipment filters, quotes, tracking events, pending settlement and manual rider offers." width="800" height="530"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Equipment flags describe vehicle profiles. Earnings stay pending through the dispute window.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The pattern behind all of these
&lt;/h2&gt;

&lt;p&gt;Every idea above has the same bones. Something needs to get from one part of the city to another. Someone is waiting for it. Someone needs to trust that it went well.&lt;/p&gt;

&lt;p&gt;Relay takes care of that part:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;filters nearby rider candidates by your rating and vehicle preferences&lt;/li&gt;
&lt;li&gt;locks a matching task quote when used before expiry&lt;/li&gt;
&lt;li&gt;provides location and stage events for your app&lt;/li&gt;
&lt;li&gt;debits a wallet or pre-authorizes a card, then records pending earnings after completion&lt;/li&gt;
&lt;li&gt;records stage progress and settlement state&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Earnings release follows the dispute window; bank withdrawals are separate.&lt;/p&gt;

&lt;p&gt;That leaves you free to work on the part that's actually yours: the idea itself.&lt;/p&gt;

&lt;h2&gt;
  
  
  Your turn
&lt;/h2&gt;

&lt;p&gt;If you want to find an idea of your own, try this:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Where do people around you wait for something to arrive?&lt;/li&gt;
&lt;li&gt;What do you regularly drive across town to fetch, or ask someone else to fetch for you?&lt;/li&gt;
&lt;li&gt;Which business near you still says "sorry, we don't deliver"?&lt;/li&gt;
&lt;li&gt;What would people rent, share, or subscribe to if getting it to them were easy?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Somewhere in those answers is a business that would have been too much trouble last year.&lt;/p&gt;

&lt;p&gt;A note on honesty: Relay is piloting in Lagos and Abuja. It supports delivery and passenger-ride tasks. Your business still does its own part, whether that's checking a prescription or deciding what your customers are allowed to send.&lt;/p&gt;

&lt;p&gt;If you build software, &lt;a href="https://dev.to/sendrelay-ng/create-your-first-delivery-task-with-relay-node-test-mode-34jm"&gt;our step-by-step guide&lt;/a&gt; walks you through your first delivery in a few lines of code. If you run a business, write to &lt;a href="mailto:hello@sendrelay.com.ng"&gt;hello@sendrelay.com.ng&lt;/a&gt; and tell us what you're trying to move. And if one of these ideas sparked a better one, tell us in the comments. We'd love to hear it.&lt;/p&gt;

&lt;p&gt;Start at &lt;a href="https://sendrelay.com.ng/?utm_source=devto&amp;amp;utm_medium=social&amp;amp;utm_campaign=devto_use_cases" rel="noopener noreferrer"&gt;sendrelay.com.ng&lt;/a&gt;.&lt;br&gt;
Next: &lt;a href="https://dev.to/sendrelay-ng/the-first-two-minutes-of-a-delivery-and-everything-after-5eg4"&gt;The first two minutes of a delivery&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Illustrations by Relay. Photos from Unsplash: Lagos market by &lt;a href="https://unsplash.com/photos/IWMQ5P9HxYM" rel="noopener noreferrer"&gt;Fahd Aminu&lt;/a&gt;, keke view by &lt;a href="https://unsplash.com/photos/t6nqZ0n3i-k" rel="noopener noreferrer"&gt;Joshua Oluwagbemiga&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>startup</category>
      <category>nigeria</category>
      <category>business</category>
      <category>ideas</category>
    </item>
    <item>
      <title>Create your first delivery task with Relay (Node, test mode)</title>
      <dc:creator>SendRelay</dc:creator>
      <pubDate>Mon, 28 Sep 2026 14:01:32 +0000</pubDate>
      <link>https://dev.to/sendrelay-ng/create-your-first-delivery-task-with-relay-node-test-mode-34jm</link>
      <guid>https://dev.to/sendrelay-ng/create-your-first-delivery-task-with-relay-node-test-mode-34jm</guid>
      <description>&lt;p&gt;New to Relay? Start with &lt;a href="https://dev.to/sendrelay-ng/where-is-my-cake-a-saturday-in-lagos-told-twice-h02"&gt;the story behind it&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Imagine you run a pharmacy app in Lagos. An order is packed, but your checkout has no delivery ID to show the customer. Your team could call riders one by one and answer “where is my order?” from a chat thread. Instead, your backend can hand the delivery to Relay when the order is ready, save the task ID, and use that ID to follow its progress. In test mode, you can build that handoff without dispatching a real rider.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fimages.unsplash.com%2Fphoto-1562793366-5f2d43dd8d9b%3Fw%3D1000%26h%3D420%26fit%3Dcrop%26crop%3Dentropy%26fm%3Djpg%26q%3D80" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fimages.unsplash.com%2Fphoto-1562793366-5f2d43dd8d9b%3Fw%3D1000%26h%3D420%26fit%3Dcrop%26crop%3Dentropy%26fm%3Djpg%26q%3D80" alt="A Lagos street scene photographed by ib daye." width="1000" height="420"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In this tutorial, you'll create a simulated package delivery with Relay's Node SDK. You'll install the SDK, use a test API key, send pickup and dropoff stages, and retrieve the task you created. The same pattern gives your order system a stable reference for later webhook handling.&lt;/p&gt;

&lt;h2&gt;
  
  
  What you'll build
&lt;/h2&gt;

&lt;p&gt;This is the core call. It creates a test task with a deliberate idempotency key, so a retry for the same order does not create another intended delivery:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;RelayClient&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@relay-sdk/sdk-node&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;relay&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;RelayClient&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;apiKey&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;RELAY_API_KEY&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;created&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;relay&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;tasks&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;taskType&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;PACKAGE_DELIVERY&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;simulationOutcome&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;SUCCESS&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;autoAssign&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;stages&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
      &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;PICKUP&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;location&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="na"&gt;latitude&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;6.5244&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="na"&gt;longitude&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;3.3792&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="na"&gt;address&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Demo pickup, Lagos&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="na"&gt;instructions&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Simulation only&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;items&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
          &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Demo parcel&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="na"&gt;estimatedValue&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;100000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="na"&gt;estimatedWeight&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;STANDARD&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="na"&gt;estimatedSize&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;BOX&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="p"&gt;],&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;DROPOFF&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;location&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="na"&gt;latitude&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;6.4654&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="na"&gt;longitude&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;3.4064&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="na"&gt;address&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Demo dropoff, Lagos&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="na"&gt;instructions&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Simulation only&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;idempotencyKey&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;order-demo-001-delivery&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;created&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;task&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;taskId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;created&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;task&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The coordinates and addresses above are demonstration inputs. &lt;code&gt;estimatedValue&lt;/code&gt; is the parcel's declared value in kobo; it is not a delivery quote. Relay's &lt;a href="https://docs.sendrelay.com.ng/tasks/create-task/" rel="noopener noreferrer"&gt;create-task guide&lt;/a&gt; covers the request fields in detail.&lt;/p&gt;

&lt;h2&gt;
  
  
  Get a test key
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://sendrelay.com.ng/?utm_source=devto&amp;amp;utm_medium=social&amp;amp;utm_campaign=devto_first_task" rel="noopener noreferrer"&gt;Create a Relay account and get an API key&lt;/a&gt;. Use a key that starts with &lt;code&gt;sk_test_&lt;/code&gt;; the current SDK and &lt;a href="https://docs.sendrelay.com.ng/getting-started/get-api-key/" rel="noopener noreferrer"&gt;API-key guide&lt;/a&gt; use that prefix for test mode. Keep the key on your server and out of source control. Never put it in browser code.&lt;/p&gt;

&lt;p&gt;For a local Node project, install the SDK and put your key in your shell environment:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install&lt;/span&gt; @relay-sdk/sdk-node
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;RELAY_API_KEY&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'sk_test_YOUR_TEST_KEY'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The placeholder is not a real credential. If you use another shell or hosting platform, set the environment variable through its secret settings. The SDK sends the key in the &lt;code&gt;X-Relay-Key&lt;/code&gt; header; you do not need to build that header yourself.&lt;/p&gt;

&lt;h2&gt;
  
  
  Create the task
&lt;/h2&gt;

&lt;p&gt;Save the first code block as &lt;code&gt;create.mjs&lt;/code&gt;, then run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;node create.mjs
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Use Node 18 or newer, as required by the Node SDK. A successful call prints the task ID and its initial status. Save &lt;code&gt;created.task.taskId&lt;/code&gt; against your own order ID. Task creation means the delivery request was accepted; it does not mean delivery is complete.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fvtvqx50pap2iir26d757.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fvtvqx50pap2iir26d757.png" alt="A sealed parcel and blank record card rest on a graphite tray." width="800" height="800"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Save the Relay task ID against your order so your backend can retrieve the delivery record.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;The request has two stages. &lt;code&gt;PICKUP&lt;/code&gt; tells Relay where the parcel begins; &lt;code&gt;DROPOFF&lt;/code&gt; tells it where the parcel should end. &lt;code&gt;simulationOutcome: 'SUCCESS'&lt;/code&gt; selects the success path for a test task. Test mode simulates the lifecycle without a real rider or real payment. You can use &lt;a href="https://docs.sendrelay.com.ng/simulation/outcomes/" rel="noopener noreferrer"&gt;simulation outcomes&lt;/a&gt; to exercise other branches after the basic path works.&lt;/p&gt;

&lt;p&gt;The second argument to &lt;code&gt;relay.tasks.create()&lt;/code&gt; sets &lt;code&gt;idempotencyKey&lt;/code&gt;. Derive this from a stable order ID in your app. If a network timeout makes you uncertain whether the create succeeded, retry the same intended delivery with the same key. For a different order, use a different key. The SDK can generate a key automatically, but an explicit order-based key gives your application control over retries.&lt;/p&gt;

&lt;p&gt;Do not add &lt;code&gt;paymentMethod: 'TEST'&lt;/code&gt; to this request. The test key selects the test flow. When you build your real order workflow, review the &lt;a href="https://docs.sendrelay.com.ng/tasks/create-task/" rel="noopener noreferrer"&gt;task creation guide&lt;/a&gt; and your payment setup before switching to a live key.&lt;/p&gt;

&lt;h2&gt;
  
  
  Read the task back
&lt;/h2&gt;

&lt;p&gt;The task ID is the link between your order and Relay's delivery state. Add this after the create call to fetch the task:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;latest&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;relay&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;tasks&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;created&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;task&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;taskId&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;task&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="nx"&gt;latest&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nx"&gt;latest&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;task&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;latest&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A test simulation can progress after the create response, so the status you read immediately may still be an early lifecycle state. Store the ID, then use task retrieval when you need the current snapshot. The &lt;a href="https://docs.sendrelay.com.ng/sdks/node-sdk/" rel="noopener noreferrer"&gt;Node SDK guide&lt;/a&gt; describes the task methods and response shape.&lt;/p&gt;

&lt;p&gt;For an application that must update a customer or operations screen when the status changes, subscribe to Relay's &lt;a href="https://docs.sendrelay.com.ng/webhooks/" rel="noopener noreferrer"&gt;webhook events&lt;/a&gt;. Verify each webhook's signature against the raw request body before using its data, and make event handling safe to repeat. Your order database should record the Relay task ID and the event IDs it has already processed. This is how the same Lagos order can move from “ready” to a delivery state your team can inspect without manual rider calls.&lt;/p&gt;

&lt;h2&gt;
  
  
  Build the next slice
&lt;/h2&gt;

&lt;p&gt;Once the success simulation works, create a new test order using &lt;code&gt;simulationOutcome: 'NO_RIDERS'&lt;/code&gt;. Decide what your checkout and support team should see when there is no available rider. Then connect the webhook to your order state and test duplicate delivery of an event before you use a live key.&lt;/p&gt;

&lt;p&gt;You now have a small, repeatable delivery integration: an order triggers one Relay task, your app saves its ID, and you can inspect its status. &lt;a href="https://sendrelay.com.ng/?utm_source=devto&amp;amp;utm_medium=social&amp;amp;utm_campaign=devto_first_task" rel="noopener noreferrer"&gt;Get your test API key&lt;/a&gt; and follow the &lt;a href="https://docs.sendrelay.com.ng/getting-started/make-first-request/" rel="noopener noreferrer"&gt;first-request guide&lt;/a&gt; to try it in your backend.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Illustrations by Relay. Lagos photo by &lt;a href="https://unsplash.com/@ib_daye" rel="noopener noreferrer"&gt;@ib_daye&lt;/a&gt; on &lt;a href="https://unsplash.com/photos/041h4gQImTM" rel="noopener noreferrer"&gt;Unsplash&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>api</category>
      <category>node</category>
      <category>javascript</category>
      <category>nigeria</category>
    </item>
    <item>
      <title>"Where is my cake?" A Saturday in Lagos, told twice</title>
      <dc:creator>SendRelay</dc:creator>
      <pubDate>Mon, 28 Sep 2026 14:01:26 +0000</pubDate>
      <link>https://dev.to/sendrelay-ng/where-is-my-cake-a-saturday-in-lagos-told-twice-h02</link>
      <guid>https://dev.to/sendrelay-ng/where-is-my-cake-a-saturday-in-lagos-told-twice-h02</guid>
      <description>&lt;p&gt;Say you bake cakes in Yaba.&lt;/p&gt;

&lt;p&gt;It stopped being a hobby a while ago. The oven is on by six, your phone buzzes with orders all day, and on Saturdays there is barely room on the table for the boxes. People find you on Instagram, fall for a photo, and send a message: &lt;em&gt;Can you do a two-tier for my mum's birthday? Lekki. Saturday, 3pm.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;You say yes. Of course you say yes.&lt;/p&gt;

&lt;h2&gt;
  
  
  The cake is the easy part
&lt;/h2&gt;

&lt;p&gt;By noon on Saturday the cake is done, and it is lovely. Now the real work starts.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fimages.unsplash.com%2Fphoto-1562793366-5f2d43dd8d9b%3Fw%3D1000%26h%3D420%26fit%3Dcrop%26crop%3Dentropy%26fm%3Djpg%26q%3D80" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fimages.unsplash.com%2Fphoto-1562793366-5f2d43dd8d9b%3Fw%3D1000%26h%3D420%26fit%3Dcrop%26crop%3Dentropy%26fm%3Djpg%26q%3D80" alt="A Lagos street scene photographed by ib daye." width="1000" height="420"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You call the rider you used last week. He doesn't pick up. You call another one, who says he's close, then names a price well above what you told the customer delivery would cost. You agree anyway, because it's already 12:40. He says he's coming. Twenty minutes later, he is still coming.&lt;/p&gt;

&lt;p&gt;When he finally arrives, you hand over the box, say a small prayer, and watch him ride off into Lagos.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fimages.unsplash.com%2Fphoto-1648023199223-25d3622bcb13%3Fw%3D1200%26fm%3Djpg%26q%3D80" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fimages.unsplash.com%2Fphoto-1648023199223-25d3622bcb13%3Fw%3D1200%26fm%3Djpg%26q%3D80" alt="Buses, cars and crowds packed into Idumota, Lagos Island, seen from above" width="1200" height="800"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Then the calls begin.&lt;/p&gt;

&lt;p&gt;At 1:30 the customer asks if the cake has left. At 2:15 she asks where it is. You call the rider. "I dey Third Mainland, I dey come." At 2:50 she calls again. The guests are arriving, and you have no honest answer for her, because you don't know where your cake is. All you have is his word.&lt;/p&gt;

&lt;p&gt;The cake arrives at 3:25. It's fine, thank God. Her afternoon wasn't, and neither was yours. You spent an hour and a half as a switchboard. You paid the rider in cash and have nothing written down to show for it. Tomorrow, you'll do it all again.&lt;/p&gt;

&lt;p&gt;If any of that felt familiar, this is for you.&lt;/p&gt;

&lt;h2&gt;
  
  
  Now play it again
&lt;/h2&gt;

&lt;p&gt;Same Saturday. Same cake. This time your orders come in through a website, and behind that website sits Relay.&lt;/p&gt;

&lt;p&gt;At noon you mark the order as ready. Your backend requests a fresh quote with the pickup, destination, vehicle and minimum-rating preferences for this delivery. It then creates the task with the same inputs and that quote ID before the quote expires. Relay uses the locked quote amount for the task.&lt;/p&gt;

&lt;p&gt;Relay filters nearby riders and offers an eligible rider the job. You haven't made a single call. Packaging and handling still need your attention.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F4gq9w96j5ghvyxvtw952.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F4gq9w96j5ghvyxvtw952.png" alt="A cake transport box rests beside a destination pin and blank record card on a tray." width="800" height="800"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;The cake is ready. Your app hands the delivery request to Relay and keeps a record of its progress.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fimages.unsplash.com%2Fphoto-1761370980969-a803951cf104%3Fw%3D1200%26fm%3Djpg%26q%3D80" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fimages.unsplash.com%2Fphoto-1761370980969-a803951cf104%3Fw%3D1200%26fm%3Djpg%26q%3D80" alt="A woman in a white T-shirt with green trim checks her phone on a street in Abuja" width="1200" height="1800"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Your website uses Relay's location and stage events to show her the delivery's progress. When her friends ask whether the cake is coming, she can check the page instead of calling you for an update.&lt;/p&gt;

&lt;p&gt;The task records the payment and delivery stages. Rider earnings stay pending through the dispute window; bank withdrawals are separate. At the end of the month you have a record to check against your orders.&lt;/p&gt;

&lt;p&gt;The cake goes to the same address either way. What changes is the afternoon you both get to have.&lt;/p&gt;

&lt;h2&gt;
  
  
  So what is Relay?
&lt;/h2&gt;

&lt;p&gt;Relay is the delivery API behind apps and websites that need to move orders across a city. Your backend creates the task, your app uses tracking events to show progress, and Relay records the delivery and settlement stages.&lt;/p&gt;

&lt;p&gt;We're starting in Lagos and Abuja.&lt;/p&gt;

&lt;h2&gt;
  
  
  When to reach for Relay
&lt;/h2&gt;

&lt;p&gt;You'll know it's time when:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;someone on your team spends part of every day calling riders&lt;/li&gt;
&lt;li&gt;customers ask "where is my order?" more often than they say thank you&lt;/li&gt;
&lt;li&gt;you've paid for a delivery and been left with nothing but a phone number&lt;/li&gt;
&lt;li&gt;you're building an app or website for food, medicine, groceries, errands, or anything else that has to reach a doorstep&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you run a business, mention Relay to whoever builds your website or app, or write to us at &lt;a href="mailto:hello@sendrelay.com.ng"&gt;hello@sendrelay.com.ng&lt;/a&gt; and we'll talk it through with you. If you're the one who builds things, &lt;a href="https://dev.to/sendrelay-ng/create-your-first-delivery-task-with-relay-node-test-mode-34jm"&gt;the next post on this page&lt;/a&gt; shows you how to send your first delivery with a few lines of code.&lt;/p&gt;

&lt;p&gt;Either way, start at &lt;a href="https://sendrelay.com.ng/?utm_source=devto&amp;amp;utm_medium=social&amp;amp;utm_campaign=devto_intro" rel="noopener noreferrer"&gt;sendrelay.com.ng&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The cake deserves to arrive calmly. So do you.&lt;/p&gt;

&lt;p&gt;Next: &lt;a href="https://dev.to/sendrelay-ng/what-would-you-build-if-delivery-were-the-easy-part-10gd"&gt;What would you build if delivery were the easy part?&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Illustrations by Relay. Photos from Unsplash: Lagos by &lt;a href="https://unsplash.com/photos/041h4gQImTM" rel="noopener noreferrer"&gt;ib daye&lt;/a&gt;, Idumota by &lt;a href="https://unsplash.com/photos/cFT_Xq4XyA0" rel="noopener noreferrer"&gt;Opeyemi Adisa&lt;/a&gt;, Abuja by &lt;a href="https://unsplash.com/photos/HSpbCiZn8Co" rel="noopener noreferrer"&gt;Muhammad-Taha Ibrahim&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>nigeria</category>
      <category>startup</category>
      <category>smallbusiness</category>
      <category>logistics</category>
    </item>
  </channel>
</rss>
