<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/">
    <channel>
        <title>Reeds developer changelog</title>
        <link>https://developer.getreeds.co/changelog</link>
        <description>Reeds Developers Blog</description>
        <lastBuildDate>Fri, 30 Oct 2026 17:00:00 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <language>en</language>
        <item>
            <title><![CDATA[The Shipping API v1 is live]]></title>
            <link>https://developer.getreeds.co/changelog/shipping-api-v1-launch</link>
            <guid>https://developer.getreeds.co/changelog/shipping-api-v1-launch</guid>
            <pubDate>Fri, 30 Oct 2026 17:00:00 GMT</pubDate>
            <description><![CDATA[Book Reeds deliveries across Lagos from your own system. Test mode opens when you sign up, and Live mode opens once Reeds approves your account.]]></description>
            <content:encoded><![CDATA[<p>The Reeds Shipping API v1 is live at <code>https://api.getreeds.co/shipping/v1</code>. From your own checkout, store or system you can save addresses, get a quote, book a delivery with that quote, and follow it through signed webhooks.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="get-started">Get started<a href="https://developer.getreeds.co/changelog/shipping-api-v1-launch#get-started" class="hash-link" aria-label="Direct link to Get started" title="Direct link to Get started" translate="no">​</a></h2>
<ol>
<li class=""><a href="https://dashboard.getreeds.co/signup" target="_blank" rel="noopener noreferrer" class="">Sign up</a> and verify your email and phone. Your account opens in Test mode, with a ₦100,000 test wallet.</li>
<li class="">Create test credentials in Dashboard → Developers, then follow the <a class="" href="https://developer.getreeds.co/docs/getting-started/quickstart">quickstart</a>.</li>
<li class="">When you're ready, complete the <a class="" href="https://developer.getreeds.co/docs/getting-started/going-live">activation checklist</a>. Reeds reviews it, and Live mode opens when your account is approved.</li>
</ol>
<p>Test and Live use the same base URL and the same token endpoint, <code>https://api.getreeds.co/oauth/token</code>. Your credentials decide the mode.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="what-you-can-use-now">What you can use now<a href="https://developer.getreeds.co/changelog/shipping-api-v1-launch#what-you-can-use-now" class="hash-link" aria-label="Direct link to What you can use now" title="Direct link to What you can use now" translate="no">​</a></h2>
<ul>
<li class=""><strong>Deliveries across Lagos.</strong> Addresses, quotes that hold their price for 24 hours, bookings, drafts for orders that aren't ready, labels, cancellations and refunds. See <a class="" href="https://developer.getreeds.co/docs/shipping/overview">Shipping</a>.</li>
<li class=""><strong>A prepaid wallet</strong> funded through Paystack. See <a class="" href="https://developer.getreeds.co/docs/billing/wallet">Wallet</a>.</li>
<li class=""><strong>Platform accounts,</strong> by application, for businesses that ship for many merchants: each delivery names the merchant, and you pay by monthly invoice. See <a class="" href="https://developer.getreeds.co/docs/platforms/overview">Platforms</a>.</li>
<li class=""><strong>Webhooks,</strong> signed and retried for two days. See <a class="" href="https://developer.getreeds.co/docs/webhooks/overview">Webhooks</a>.</li>
<li class=""><strong>A sandbox</strong> with a delivery simulator and real webhooks. See <a class="" href="https://developer.getreeds.co/docs/testing/sandbox">Sandbox</a>.</li>
<li class=""><strong>The <a class="" href="https://developer.getreeds.co/api">API Reference</a>.</strong> Send a request from the page with Test Request, using test credentials. The API refuses live credentials from a browser.</li>
<li class=""><strong>The <a href="https://developer.getreeds.co/openapi/shipping-v1.yaml" target="_blank" rel="noopener noreferrer" class="">OpenAPI file</a> and a <a href="https://developer.getreeds.co/downloads/reeds-shipping-v1.postman_collection.json" target="_blank" rel="noopener noreferrer" class="">Postman collection</a>.</strong></li>
</ul>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="what-isnt-included">What isn't included<a href="https://developer.getreeds.co/changelog/shipping-api-v1-launch#what-isnt-included" class="hash-link" aria-label="Direct link to What isn't included" title="Direct link to What isn't included" translate="no">​</a></h2>
<ul>
<li class=""><strong>Inter-state delivery.</strong> <code>GET /coverage</code> lists Lagos to the FCT with <code>available: false</code>, and a quote on that route returns <code>INTERSTATE_NOT_AVAILABLE</code>.</li>
<li class=""><strong>Official SDKs and plugins.</strong> The API is plain JSON over HTTPS, so any HTTP client works. See <a class="" href="https://developer.getreeds.co/docs/resources/libraries">Libraries and plugins</a>.</li>
<li class=""><strong>A status page.</strong> If you suspect an outage, email <a href="mailto:support@getreeds.co" target="_blank" rel="noopener noreferrer" class="">support@getreeds.co</a>.</li>
</ul>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="how-well-announce-changes">How we'll announce changes<a href="https://developer.getreeds.co/changelog/shipping-api-v1-launch#how-well-announce-changes" class="hash-link" aria-label="Direct link to How we'll announce changes" title="Direct link to How we'll announce changes" translate="no">​</a></h2>
<p>Every change to a public Reeds API gets an entry here. Additive changes ship within <code>/v1</code>, and breaking changes go to <code>/v2</code> with six months of overlap. See <a class="" href="https://developer.getreeds.co/docs/api-basics/versioning">Versioning</a>.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[The delivery promise is now a two-day range]]></title>
            <link>https://developer.getreeds.co/changelog/delivery-promise-range</link>
            <guid>https://developer.getreeds.co/changelog/delivery-promise-range</guid>
            <pubDate>Fri, 30 Oct 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[Within a state, schedule.earliest_delivery now carries the first day of a two-day delivery range, and the booking cut-offs are 13:45 Monday to Friday and 11:45 on Saturday.]]></description>
            <content:encoded><![CDATA[<p>Within a state, the <a class="" href="https://developer.getreeds.co/docs/shipping/delivery-promise">delivery promise</a> is now a two-day range, and the cut-offs have moved.</p>
<ul>
<li class=""><strong>The cut-offs.</strong> Booked and ready before 13:45 Monday to Friday, or before 11:45 on a Saturday: delivered that day or the next working day. At or after the cut-off: the next working day or the working day after. The old cut-off was 16:30 every working day.</li>
<li class=""><strong>Sundays and public holidays</strong> are still off. Booked on one, the delivery is the next working day or the working day after.</li>
<li class=""><strong><code>schedule.earliest_delivery</code> is filled on <code>standard</code> quotes, deliveries and webhooks.</strong> It is the range's first day, and <code>deliver_by</code> is the last. It used to be <code>null</code> outside inter-state routes. It is written at close of business (19:00) like the other two, so read the day: a delivery can arrive at any time on it.</li>
<li class=""><strong><code>null</code> still happens.</strong> A delivery booked before this change has no earliest date, and after a <code>delivery.schedule_changed</code> the new <code>deliver_by</code> is the whole promise, so <code>earliest_delivery</code> is <code>null</code> and <code>previous_schedule</code> keeps the range. Show <code>deliver_by</code> alone when it is <code>null</code>.</li>
</ul>
<p><code>pickup_by</code> and <code>deliver_by</code> keep their meaning, and close of business is still 19:00, but a delivery booked between 13:45 and 16:30 now gets later dates than it did. Nothing is removed or renamed.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Test addresses corrected, and other small fixes to the docs]]></title>
            <link>https://developer.getreeds.co/changelog/test-addresses-and-corrections</link>
            <guid>https://developer.getreeds.co/changelog/test-addresses-and-corrections</guid>
            <pubDate>Fri, 30 Oct 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[Test mode has no special address strings. The guides now show what to send to reach each outcome, and a few details now match what the API returns.]]></description>
            <content:encoded><![CDATA[<p>We checked these docs against the running API and corrected what didn't match.</p>
<ul>
<li class=""><strong>Test addresses.</strong> Test mode treats every address as Live does, so <a class="" href="https://developer.getreeds.co/docs/testing/test-addresses">Test addresses</a> now shows what to send for each outcome, and the <a class="" href="https://developer.getreeds.co/docs/getting-started/quickstart">quickstart</a> saves its addresses with map pins.</li>
<li class=""><strong><code>wallet.funded</code> carries <code>reference</code>.</strong> The webhook now has the <code>reference</code> that <code>POST /wallet/fund</code> returned, so you can match it to the top-up you started. See <a class="" href="https://developer.getreeds.co/docs/webhooks/events">Events</a>. The amount limits, ₦1,000 to ₦100,000,000, are now in the <a class="" href="https://developer.getreeds.co/docs/billing/wallet">wallet guide</a>.</li>
<li class=""><strong>Invoicing.</strong> Monthly invoicing is for <a class="" href="https://developer.getreeds.co/docs/billing/invoices">platform accounts</a>. <code>GET /invoices</code> also accepts <code>status=void</code>.</li>
<li class=""><strong>Examples.</strong> <code>GET /wallet</code> shows every field it returns, the <code>cancellation</code> block shows <code>note</code>, and <code>GET /coverage</code> lists Lagos as the covered state, with the FCT as a route.</li>
<li class=""><strong>Ids.</strong> <code>txn_</code> and <code>inv_</code> are listed with the other prefixes, and real ids are longer than the examples: allow 64 characters.</li>
</ul>
<p>The <code>reference</code> is the one change to the API, and it only adds a field. Everything else here corrects the docs.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Addresses and quotes, and hold_reason on ACCOUNT_ON_HOLD]]></title>
            <link>https://developer.getreeds.co/changelog/addresses-and-quotes</link>
            <guid>https://developer.getreeds.co/changelog/addresses-and-quotes</guid>
            <pubDate>Thu, 29 Oct 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[How saved addresses, address confirmation and one-shot quotes behave in Test and Live mode; ACCOUNT_ON_HOLD carries hold_reason.]]></description>
            <content:encoded><![CDATA[<p>How the address and quote endpoints behave in v1, in Test and Live mode.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="addresses">Addresses<a href="https://developer.getreeds.co/changelog/addresses-and-quotes#addresses" class="hash-link" aria-label="Direct link to Addresses" title="Direct link to Addresses" translate="no">​</a></h2>
<ul>
<li class=""><a class="" href="https://developer.getreeds.co/docs/shipping/addresses"><code>POST /addresses</code></a> resolves an address from a <code>place_id</code>, your <code>latitude</code> and <code>longitude</code>, or the text. A precise place or your coordinates are <code>verified</code>; an area, a whole road, or a text match that is only approximate is <code>needs_confirmation</code> with Reeds' <code>suggestion</code>. <code>match.confidence</code> is <code>exact</code>, <code>approximate</code>, <code>none</code> or <code>confirmed</code>.</li>
<li class="">Saving the same place with the same contact again returns the existing address as it is. Use <code>PATCH /addresses/{id}</code> to change its contact, <code>notes</code> or <code>metadata</code>; sending <code>address</code>, <code>place_id</code>, <code>latitude</code> or <code>longitude</code> there is <code>422 INVALID_REQUEST</code>.</li>
<li class="">An address placed from your coordinates has <code>location.place_id: null</code>.</li>
<li class=""><code>POST /addresses/{id}/confirm</code> takes exactly one of <code>accept_suggestion: true</code>, a <code>place_id</code>, or <code>latitude</code> and <code>longitude</code>, and sends the <code>address.confirmed</code> webhook. An address that is already verified gets <code>409 ACTION_NOT_ALLOWED</code>.</li>
<li class=""><code>DELETE /addresses/{id}</code> expires the address's unused quotes at once, and is refused with <code>409 ADDRESS_IN_USE</code> while a draft uses it.</li>
<li class=""><code>GET /addresses</code> lists saved addresses only; addresses saved inline by <code>POST /quotes</code> are not listed (read them by id), and are deleted 30 days after their last use if no delivery was made from them.</li>
<li class=""><code>metadata</code> takes up to 20 keys of up to 40 characters, with string values of up to 500.</li>
</ul>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="quotes">Quotes<a href="https://developer.getreeds.co/changelog/addresses-and-quotes#quotes" class="hash-link" aria-label="Direct link to Quotes" title="Direct link to Quotes" translate="no">​</a></h2>
<ul>
<li class=""><a class="" href="https://developer.getreeds.co/docs/shipping/quotes"><code>POST /quotes</code></a> returns <code>201</code> with a <code>qt_</code> quote that lasts 24 hours and books one delivery, or <code>200</code> with <code>available: false</code> and a reason when Reeds can't serve the route. When Reeds can't measure the route at all, it is <code>503 SERVICE_UNAVAILABLE</code>: retry shortly.</li>
<li class=""><code>pickup_date</code> is Lagos today up to 7 days ahead; outside that range is <code>422 PICKUP_DATE_OUT_OF_RANGE</code>, with the field in <code>error.fields</code>. The quote's <code>schedule</code> counts from that day.</li>
</ul>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="account_on_hold">ACCOUNT_ON_HOLD<a href="https://developer.getreeds.co/changelog/addresses-and-quotes#account_on_hold" class="hash-link" aria-label="Direct link to ACCOUNT_ON_HOLD" title="Direct link to ACCOUNT_ON_HOLD" translate="no">​</a></h2>
<p><a class="" href="https://developer.getreeds.co/errors/ACCOUNT_ON_HOLD"><code>ACCOUNT_ON_HOLD</code></a> now documents <code>error.hold_reason</code>: <code>overdue_invoice</code> (pay the overdue invoice) or <code>reeds</code> (contact Reeds). Branch on it, not on the message.</p>
<p>Nothing is removed or renamed. Clients that ignore unknown fields need no change.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Deliveries, wallet funding and the sandbox]]></title>
            <link>https://developer.getreeds.co/changelog/deliveries-money-and-sandbox</link>
            <guid>https://developer.getreeds.co/changelog/deliveries-money-and-sandbox</guid>
            <pubDate>Thu, 29 Oct 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[Book from a quote or create a draft, change, book, ready and cancel deliveries, fund the live wallet, and use the sandbox endpoints; Idempotency-Key is released on 401, 402, 403, 409 and 429.]]></description>
            <content:encoded><![CDATA[<p>How the delivery and money endpoints behave in v1, in Test and Live mode.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="deliveries">Deliveries<a href="https://developer.getreeds.co/changelog/deliveries-money-and-sandbox#deliveries" class="hash-link" aria-label="Direct link to Deliveries" title="Direct link to Deliveries" translate="no">​</a></h2>
<ul>
<li class=""><a class="" href="https://developer.getreeds.co/docs/shipping/book-a-delivery"><code>POST /deliveries</code></a> books from a quote (<code>quote_id</code>): <code>201</code> with status <code>scheduled</code>, the tracking code and the wallet debited at once. A quote books one delivery: using it again is <code>409 QUOTE_ALREADY_USED</code> with <code>error.delivery_id</code>, and an expired quote (or one whose address you deleted) is <code>409 QUOTE_EXPIRED</code>.</li>
<li class="">If the wallet doesn't cover it you get <code>402 INSUFFICIENT_BALANCE</code> (<code>402 CREDIT_LIMIT_REACHED</code> for an invoice account): nothing is created and the quote stays unused. Fund the wallet and retry with the same <code>Idempotency-Key</code>.</li>
<li class="">Leave out <code>quote_id</code> to <a class="" href="https://developer.getreeds.co/docs/shipping/drafts-and-requires-action">create a draft</a> from two saved addresses in any status. A draft never gets a <code>402</code>: it waits with <code>CONFIRM_PICKUP_ADDRESS</code>, <code>CONFIRM_DROPOFF_ADDRESS</code>, <code>AWAITING_PRICE</code>, <code>BOOK</code> or <code>FUND_WALLET</code>. With <code>auto_book: true</code> it books itself once it is priced and the price is within <code>max_total</code>; above <code>max_total</code> it waits with <code>BOOK</code>. <code>max_total</code> never limits <code>POST /deliveries/{id}/book</code>.</li>
<li class=""><code>pickup_instructions</code> and <code>dropoff_instructions</code> (up to 500 characters) are for that delivery only; the rider sees them before the address's own notes. <code>PATCH /deliveries/{id}</code> changes them, <code>reference</code> and <code>metadata</code> (<code>null</code> clears a field): the pickup instructions until the parcel is collected, the drop-off instructions until it is delivered, and never on a cancelled delivery (<code>409 ACTION_NOT_ALLOWED</code>). Sending an address or the package is <code>422</code>.</li>
<li class=""><code>POST /deliveries/{id}/book</code> (<code>402</code> when short, <code>409 ACTION_NOT_ALLOWED</code> when not priced or already booked, <code>503</code> while live booking is paused), <code>POST /deliveries/{id}/ready</code> (<code>409</code> on a delivered or cancelled delivery) and <code>POST /deliveries/{id}/cancel</code> (<code>reason</code> becomes <code>cancellation.note</code>; <code>409 CANCEL_NOT_ALLOWED</code> once the rider has set off) answer.</li>
<li class="">A delivery booked as it is created sends <code>delivery.created</code> (already <code>scheduled</code>) then <code>delivery.booked</code>. A fresh draft's <code>events</code> is empty: the timeline lists statuses the delivery moved to.</li>
</ul>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="idempotency">Idempotency<a href="https://developer.getreeds.co/changelog/deliveries-money-and-sandbox#idempotency" class="hash-link" aria-label="Direct link to Idempotency" title="Direct link to Idempotency" translate="no">​</a></h2>
<p>Requests that fail with <code>401</code>, <code>402</code>, <code>403</code>, <code>409</code> or <code>429</code> release the <code>Idempotency-Key</code>, so retry with it once the cause is fixed. <code>400</code>, <code>404</code> and <code>422</code> answers are saved and replayed with <code>Idempotent-Replayed: true</code>, like a success. A key you also used on the dashboard is <code>422 IDEMPOTENCY_KEY_REUSED</code>.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="money">Money<a href="https://developer.getreeds.co/changelog/deliveries-money-and-sandbox#money" class="hash-link" aria-label="Direct link to Money" title="Direct link to Money" translate="no">​</a></h2>
<ul>
<li class=""><a class="" href="https://developer.getreeds.co/docs/billing/wallet"><code>POST /wallet/fund</code></a> returns <code>{ amount, currency, payment_url, reference }</code>. Your credentials act for the account: any live credentials can start a top-up, and Paystack sends the receipt to the account Owner. A test token, or an invoice account, gets <code>409 ACTION_NOT_ALLOWED</code>.</li>
<li class=""><a class="" href="https://developer.getreeds.co/docs/testing/sandbox"><code>POST /sandbox/wallet/fund</code></a> credits the test wallet at once and books drafts waiting on <code>FUND_WALLET</code>. An <code>Idempotency-Key</code> is optional there: a retry with the same key never credits twice. Invoice accounts get <code>409</code>.</li>
<li class=""><a class="" href="https://developer.getreeds.co/docs/testing/sandbox"><code>POST /sandbox/deliveries/{id}/advance</code></a> moves a sandbox delivery and fires the real webhooks. Both sandbox endpoints answer <code>403 TEST_MODE_ONLY</code> to live credentials.</li>
</ul>
<p>Nothing is removed or renamed. Clients that ignore unknown fields need no change.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[How the live IP allowlist is checked, and webhook endpoints Reeds turns off]]></title>
            <link>https://developer.getreeds.co/changelog/ip-allowlist-enforced</link>
            <guid>https://developer.getreeds.co/changelog/ip-allowlist-enforced</guid>
            <pubDate>Thu, 29 Oct 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[The optional live IP allowlist is checked on every live token request and API call. Also, POST /webhooks/test answers 409 while Reeds has turned your endpoint off.]]></description>
            <content:encoded><![CDATA[<p>How the API checks the <a class="" href="https://developer.getreeds.co/docs/getting-started/authentication#recommended-restrict-live-credentials-by-ip">live IP allowlist</a> announced on 28 October. It is optional, and off while your list is empty.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="what-the-api-checks">What the API checks<a href="https://developer.getreeds.co/changelog/ip-allowlist-enforced#what-the-api-checks" class="hash-link" aria-label="Direct link to What the API checks" title="Direct link to What the API checks" translate="no">​</a></h2>
<ul>
<li class="">With at least one address on your list, <code>POST /oauth/token</code> with a live client id from any other address returns <code>400</code> with <code>{ "error": "unauthorized_client" }</code>, and the secret isn't checked. Every <code>/shipping/v1/*</code> call with a live token from any other address returns <code>403</code> <a class="" href="https://developer.getreeds.co/errors/IP_NOT_ALLOWED"><code>IP_NOT_ALLOWED</code></a>, naming the address Reeds saw, before anything runs.</li>
<li class="">Test credentials and test tokens are never checked.</li>
<li class="">Reeds reads your address from its own proxy, never from an <code>X-Forwarded-For</code> header you send. An IPv4 address that reaches Reeds as IPv6 (<code>::ffff:41.58.10.5</code>) is matched as the IPv4 address.</li>
<li class="">A change takes effect within a minute.</li>
<li class="">Refused calls are in the dashboard's API request log, and the addresses they came from are in the allowlist card's <strong>Recent IP addresses</strong>, marked as refused.</li>
</ul>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="changing-the-list">Changing the list<a href="https://developer.getreeds.co/changelog/ip-allowlist-enforced#changing-the-list" class="hash-link" aria-label="Direct link to Changing the list" title="Direct link to Changing the list" translate="no">​</a></h2>
<ul>
<li class="">Adding and removing an address each need a 6-digit code from your authenticator app (recovery codes aren't accepted), so turn on two-factor authentication first. After 5 wrong codes in 15 minutes, that person's changes are paused for up to 15 minutes.</li>
<li class="">The account's Owners get an email for every change: who made it, when, from which IP address, and whether it turned the list on or off.</li>
<li class="">Removing an address always works, even while adding is paused.</li>
</ul>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="webhook-endpoints-reeds-turns-off">Webhook endpoints Reeds turns off<a href="https://developer.getreeds.co/changelog/ip-allowlist-enforced#webhook-endpoints-reeds-turns-off" class="hash-link" aria-label="Direct link to Webhook endpoints Reeds turns off" title="Direct link to Webhook endpoints Reeds turns off" translate="no">​</a></h2>
<p>Reeds can turn a webhook endpoint off, for example while investigating a problem with it. Your audit log says why. Nothing is sent to it then, not even a resend or a test event, and saving the URL again doesn't turn it back on; <a class="" href="https://developer.getreeds.co/docs/resources/support">contact support</a>. While it is off, <a class="" href="https://developer.getreeds.co/docs/webhooks/overview#send-a-test-event"><code>POST /webhooks/test</code></a> returns <code>409 ACTION_NOT_ALLOWED</code>. An endpoint that was disabled after failing for 3 days works as before: save its URL again to turn it back on.</p>
<p>Nothing is removed or renamed.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[When wallet.low_balance is sent]]></title>
            <link>https://developer.getreeds.co/changelog/low-balance-webhook-timing</link>
            <guid>https://developer.getreeds.co/changelog/low-balance-webhook-timing</guid>
            <pubDate>Thu, 29 Oct 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[wallet.low_balance is sent once when a debit takes the balance to or below your threshold, and again only after the balance has recovered past a margin.]]></description>
            <content:encoded><![CDATA[<p>The <a class="" href="https://developer.getreeds.co/docs/webhooks/events"><code>wallet.low_balance</code></a> webhook now documents exactly when it is sent. Its payload is unchanged: <code>balance</code>, <code>low_balance_threshold</code> and <code>currency</code>.</p>
<ul>
<li class="">It is sent <strong>once</strong>, by the debit that takes the balance to <strong>or below</strong> the threshold you set in the dashboard. A batch of bookings that crosses it sends one event, not one per booking.</li>
<li class="">It is <strong>not sent again</strong> until the balance has risen back to the threshold plus a margin (₦1,000 or 10% of the threshold, whichever is more) and fallen again, and at most once an hour. A small refund that lifts the balance just over the line doesn't count as recovering.</li>
<li class=""><strong>Changing the threshold</strong> re-arms it without sending one: the next debit at or below the new threshold sends it.</li>
<li class="">It is sent in <strong>test mode</strong> too, so you can test your handler. The email to the account's Owners and Admins goes in live mode only.</li>
</ul>
<p>Nothing is removed or renamed.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Webhooks]]></title>
            <link>https://developer.getreeds.co/changelog/webhooks</link>
            <guid>https://developer.getreeds.co/changelog/webhooks</guid>
            <pubDate>Thu, 29 Oct 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[Save an endpoint per mode in the dashboard and Reeds posts signed events for deliveries, addresses, the wallet and invoices, with retries for about 2 days, an event log with Resend, and POST /webhooks/test.]]></description>
            <content:encoded><![CDATA[<p><a class="" href="https://developer.getreeds.co/docs/webhooks/overview">Webhooks</a> send in Test and Live mode, for every event type in the <a class="" href="https://developer.getreeds.co/docs/webhooks/events">list</a>.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="the-endpoint">The endpoint<a href="https://developer.getreeds.co/changelog/webhooks#the-endpoint" class="hash-link" aria-label="Direct link to The endpoint" title="Direct link to The endpoint" translate="no">​</a></h2>
<ul>
<li class="">Save one HTTPS endpoint per mode in Dashboard → Developers. Reeds checks the URL when you save it and again on every send: it must use a host name (not an IP address, <code>localhost</code> or an internal name) that resolves only to public addresses, and each send goes to the address Reeds checked. Redirects are never followed.</li>
<li class="">The signing secret (<code>whsec_test_...</code> or <code>whsec_live_...</code>) is shown in the dashboard. <a class="" href="https://developer.getreeds.co/docs/webhooks/verify-signatures">Rotating it</a> keeps the old secret signing for 24 hours: each event then carries two <code>v1</code> values in <code>Reeds-Signature</code>, so a receiver holding either secret verifies. Rotating again within the 24 hours drops the oldest secret.</li>
</ul>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="sending-and-retries">Sending and retries<a href="https://developer.getreeds.co/changelog/webhooks#sending-and-retries" class="hash-link" aria-label="Direct link to Sending and retries" title="Direct link to Sending and retries" translate="no">​</a></h2>
<ul>
<li class="">Every event is a <code>POST</code> with <code>Reeds-Event-Id</code>, <code>Reeds-Event-Type</code> and <code>Reeds-Signature</code>, signed afresh on each attempt. Reply <code>2xx</code> within 10 seconds.</li>
<li class="">A failed attempt (an error status, a redirect, a timeout, a connection or TLS failure) is retried 1 minute, 5 minutes, 30 minutes, 2 hours, 6 hours, 12 hours and 24 hours after the previous attempt, each with up to 15 seconds of jitter. After the 8th attempt the event is failed.</li>
<li class="">An endpoint that keeps failing for 3 days is disabled: waiting events are marked failed, and the account's Owners, Admins and Developers are emailed. Save its URL again to turn it back on.</li>
</ul>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="the-event-log-and-test-events">The event log and test events<a href="https://developer.getreeds.co/changelog/webhooks#the-event-log-and-test-events" class="hash-link" aria-label="Direct link to The event log and test events" title="Direct link to The event log and test events" translate="no">​</a></h2>
<ul>
<li class="">The dashboard's event log shows every event, its attempts and your server's answers for 30 days. <strong>Resend</strong> makes one more attempt at once, without changing the retry schedule, even while the endpoint is disabled.</li>
<li class=""><a class="" href="https://developer.getreeds.co/docs/webhooks/overview#send-a-test-event"><code>POST /webhooks/test</code></a> takes an optional <code>type</code> (any event type; <code>delivery.status_changed</code> by default) and answers <code>{ event_id, type, sent }</code>. A test event is sent once and never retried. No endpoint for the mode, or an unknown <code>type</code>, is <code>422 INVALID_REQUEST</code>.</li>
</ul>
<p>Nothing is removed or renamed. Clients that ignore unknown fields and event types need no change.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Invoice numbers, void, the invoice PDF, and two new webhooks]]></title>
            <link>https://developer.getreeds.co/changelog/invoices-paid-overdue-and-pdf</link>
            <guid>https://developer.getreeds.co/changelog/invoices-paid-overdue-and-pdf</guid>
            <pubDate>Wed, 28 Oct 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[Invoices gain a number and the amounts paid, credited and due, a void status and a PDF; invoice.paid and invoice.overdue join invoice.issued.]]></description>
            <content:encoded><![CDATA[<p>For platform accounts billed monthly, invoices now carry everything you need to reconcile them.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="the-invoice-object">The Invoice object<a href="https://developer.getreeds.co/changelog/invoices-paid-overdue-and-pdf#the-invoice-object" class="hash-link" aria-label="Direct link to The Invoice object" title="Direct link to The Invoice object" translate="no">​</a></h2>
<ul>
<li class=""><code>number</code>: <code>RDS-2026-000123</code> (<code>TEST-2026-000123</code> in test mode), numbered per mode and year with no gaps.</li>
<li class=""><code>discounts</code> and <code>adjustments</code> (credit notes, debit notes, credit carried from an earlier invoice), so <code>delivery_fees + vat + service_charges - discounts + adjustments = total</code>.</li>
<li class=""><code>amount_paid</code>, <code>amount_credited</code> and <code>amount_due</code>: what is still owed after payments and credits.</li>
<li class=""><code>voided_at</code>, and a new status, <code>void</code>: Reeds cancelled the invoice and it owes nothing. A corrected one is issued in its place with a new number. <code>GET /invoices?status=void</code> lists them.</li>
</ul>
<p>VAT is still on the delivery fee only; the service charge carries none.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="the-pdf">The PDF<a href="https://developer.getreeds.co/changelog/invoices-paid-overdue-and-pdf#the-pdf" class="hash-link" aria-label="Direct link to The PDF" title="Direct link to The PDF" translate="no">​</a></h2>
<p><code>GET /invoices/{id}/pdf</code> returns the invoice as an A4 PDF, with every line. It answers <code>304</code> to a matching <code>If-None-Match</code>.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="new-webhooks">New webhooks<a href="https://developer.getreeds.co/changelog/invoices-paid-overdue-and-pdf#new-webhooks" class="hash-link" aria-label="Direct link to New webhooks" title="Direct link to New webhooks" translate="no">​</a></h2>
<ul>
<li class=""><a class="" href="https://developer.getreeds.co/docs/webhooks/events"><code>invoice.paid</code></a>: an invoice was paid in full, by a payment, a transfer Reeds recorded, or a credit that settled it.</li>
<li class=""><a class="" href="https://developer.getreeds.co/docs/webhooks/events"><code>invoice.overdue</code></a>: an invoice passed its due date unpaid. The account is on hold until it is paid: new bookings get <code>403 ACCOUNT_ON_HOLD</code>.</li>
</ul>
<p>Both carry <code>data.invoice</code>, like <code>invoice.issued</code>.</p>
<p>Nothing is removed or renamed. Clients that ignore unknown fields and unknown event types need no change.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Optional IP allowlist for live credentials]]></title>
            <link>https://developer.getreeds.co/changelog/live-ip-allowlist</link>
            <guid>https://developer.getreeds.co/changelog/live-ip-allowlist</guid>
            <pubDate>Wed, 28 Oct 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[Limit your live credentials to your own servers' IP addresses. Optional, off by default.]]></description>
            <content:encoded><![CDATA[<p>You can now limit your <strong>live</strong> credentials to the IP addresses of your own servers. It is optional and off by default, so nothing changes for you unless you turn it on. We recommend it.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="what-changes-once-it-is-on">What changes once it is on<a href="https://developer.getreeds.co/changelog/live-ip-allowlist#what-changes-once-it-is-on" class="hash-link" aria-label="Direct link to What changes once it is on" title="Direct link to What changes once it is on" translate="no">​</a></h2>
<ul>
<li class=""><code>POST /oauth/token</code> with a live client id from an address that isn't on the list returns <code>400</code> with <code>{ "error": "unauthorized_client" }</code>, and no token is issued.</li>
<li class="">Every <code>/shipping/v1/*</code> call with a live token from an address that isn't on the list returns <code>403</code> with the new code <a class="" href="https://developer.getreeds.co/errors/IP_NOT_ALLOWED"><code>IP_NOT_ALLOWED</code></a>.</li>
<li class="">Test credentials and test tokens are never restricted.</li>
</ul>
<p>An empty list accepts any address, as before. Set it up in the dashboard under Developers, in Live mode. See <a class="" href="https://developer.getreeds.co/docs/getting-started/authentication#recommended-restrict-live-credentials-by-ip">Authentication</a>.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Parcel limits, prohibited items, liability and the merchant filter]]></title>
            <link>https://developer.getreeds.co/changelog/parcel-limits-and-platform-merchants</link>
            <guid>https://developer.getreeds.co/changelog/parcel-limits-and-platform-merchants</guid>
            <pubDate>Wed, 28 Oct 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[Four new error codes for what Reeds carries, an optional package.dimensions_cm, a liability field on every delivery, and merchant_id on the delivery list.]]></description>
            <content:encoded><![CDATA[<p>What Reeds carries is now part of the contract, and platforms can filter by merchant.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="new-error-codes-422">New error codes (422)<a href="https://developer.getreeds.co/changelog/parcel-limits-and-platform-merchants#new-error-codes-422" class="hash-link" aria-label="Direct link to New error codes (422)" title="Direct link to New error codes (422)" translate="no">​</a></h2>
<ul>
<li class=""><a class="" href="https://developer.getreeds.co/errors/PARCEL_TOO_HEAVY"><code>PARCEL_TOO_HEAVY</code></a>: <code>package.weight_kg</code> over 30 kg.</li>
<li class=""><a class="" href="https://developer.getreeds.co/errors/PARCEL_TOO_LARGE"><code>PARCEL_TOO_LARGE</code></a>: the longest side in the new, optional <code>package.dimensions_cm</code> (<code>length</code>, <code>width</code>, <code>height</code>, in centimetres) over 120 cm.</li>
<li class=""><a class="" href="https://developer.getreeds.co/errors/PROHIBITED_ITEM"><code>PROHIBITED_ITEM</code></a>: the description names something Reeds can't carry (cash, weapons, drugs, hazardous goods such as petrol or gas cylinders, perishable food). The message names the category.</li>
<li class=""><a class="" href="https://developer.getreeds.co/errors/SHIPPER_REQUIRED"><code>SHIPPER_REQUIRED</code></a>: a platform account created a delivery without <code>shipper</code>.</li>
</ul>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="liability">Liability<a href="https://developer.getreeds.co/changelog/parcel-limits-and-platform-merchants#liability" class="hash-link" aria-label="Direct link to Liability" title="Direct link to Liability" translate="no">​</a></h2>
<p>Every delivery now has <code>liability: { covered_amount, cap }</code> (kobo): what Reeds covers if the parcel is lost or damaged, which is the declared value up to ₦50,000. A higher <code>declared_value</code> is still accepted.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="merchants">Merchants<a href="https://developer.getreeds.co/changelog/parcel-limits-and-platform-merchants#merchants" class="hash-link" aria-label="Direct link to Merchants" title="Direct link to Merchants" translate="no">​</a></h2>
<p><code>GET /deliveries</code> takes <code>merchant_id</code>: platform accounts get only that merchant's deliveries (<code>shipper.merchant_id</code>).</p>
<p>Nothing is removed or renamed. Clients that ignore unknown fields need no change.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Sandbox data is now kept for 90 days]]></title>
            <link>https://developer.getreeds.co/changelog/sandbox-retention</link>
            <guid>https://developer.getreeds.co/changelog/sandbox-retention</guid>
            <pubDate>Wed, 28 Oct 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[Test-mode deliveries, events and wallet history are kept for 90 days instead of 30.]]></description>
            <content:encoded><![CDATA[<p>Sandbox (test-mode) data is now kept for <strong>90 days</strong>. Earlier text said 30 days. Live data is unaffected, and your sandbox wallet's starting balance is never removed. See <a class="" href="https://developer.getreeds.co/docs/testing/sandbox">Sandbox</a>.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Clearer rules for retries and webhook failures]]></title>
            <link>https://developer.getreeds.co/changelog/idempotency-and-webhook-wording</link>
            <guid>https://developer.getreeds.co/changelog/idempotency-and-webhook-wording</guid>
            <pubDate>Tue, 27 Oct 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[The API reference now says exactly when a 5xx releases an Idempotency-Key, and what happens after the last webhook retry.]]></description>
            <content:encoded><![CDATA[<p>Two clarifications in the <a class="" href="https://developer.getreeds.co/api">API reference</a>; the API's behaviour is unchanged.</p>
<ul>
<li class=""><strong>Idempotency-Key and 5xx errors.</strong> A 5xx releases the key only if nothing was saved. If the work was saved and the request failed afterwards, a retry with the same key replays the saved result, like any success. See <a class="" href="https://developer.getreeds.co/docs/api-basics/idempotency">Idempotency</a>.</li>
<li class=""><strong>Webhook retries.</strong> After the last retry (24 hours), the event is marked failed. An endpoint that keeps failing for 3 days is disabled, and the account is emailed.</li>
</ul>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Postman collection]]></title>
            <link>https://developer.getreeds.co/changelog/postman-collection</link>
            <guid>https://developer.getreeds.co/changelog/postman-collection</guid>
            <pubDate>Tue, 27 Oct 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[A ready-made Postman collection for the Shipping API, with OAuth already set up.]]></description>
            <content:encoded><![CDATA[<p>You can now <a href="https://developer.getreeds.co/downloads/reeds-shipping-v1.postman_collection.json" target="_blank" rel="noopener noreferrer" class="">download a Postman collection</a> of the Shipping API from <a class="" href="https://developer.getreeds.co/docs/resources/libraries">Libraries and plugins</a>.</p>
<p>It has every endpoint, grouped like the API reference, with OAuth 2.0 client credentials set up: put your test client id and secret in the collection variables and Postman fetches the access token for you. It is generated from the <a href="https://developer.getreeds.co/openapi/shipping-v1.yaml" target="_blank" rel="noopener noreferrer" class="">OpenAPI file</a> each time these docs are published, so the two always match.</p>]]></content:encoded>
        </item>
    </channel>
</rss>