curl --fail-with-body -X POST "https://maatstays.wtf/v1/webhooks/deliveries/6f1a2b3c-4d5e-4f60-8718-9a0b1c2d3e4f/redrive" \
-H "Authorization: Bearer $MAAT_PARTNER_TOKEN"const res = await fetch(
'https://maatstays.wtf/v1/webhooks/deliveries/6f1a2b3c-4d5e-4f60-8718-9a0b1c2d3e4f/redrive',
{
method: 'POST',
headers: { Authorization: `Bearer ${process.env.MAAT_PARTNER_TOKEN}` },
},
);
const body = await res.json();
if (!res.ok) throw new Error(`${body.code}: ${body.detail}`);
// Same event_id — your idempotency key survives the replay.
console.log(body.delivery.status, body.redrives_remaining_today);
import os
import requests
res = requests.post(
"https://maatstays.wtf/v1/webhooks/deliveries/6f1a2b3c-4d5e-4f60-8718-9a0b1c2d3e4f/redrive",
headers={"Authorization": f"Bearer {os.environ['MAAT_PARTNER_TOKEN']}"},
timeout=30,
)
body = res.json()
if res.status_code >= 400:
raise RuntimeError(f"{body['code']}: {body.get('detail')}")
# Same event_id — your idempotency key survives the replay.
print(body["delivery"]["status"], body["redrives_remaining_today"])
package main
import (
"encoding/json"
"fmt"
"log"
"net/http"
"os"
)
type deliveryView struct {
Status string `json:"status"`
}
type payload struct {
Delivery deliveryView `json:"delivery"`
RedrivesRemainingToday int `json:"redrives_remaining_today"`
}
func main() {
req, _ := http.NewRequest(http.MethodPost,
"https://maatstays.wtf/v1/webhooks/deliveries/6f1a2b3c-4d5e-4f60-8718-9a0b1c2d3e4f/redrive", nil)
req.Header.Set("Authorization", "Bearer "+os.Getenv("MAAT_PARTNER_TOKEN"))
res, err := http.DefaultClient.Do(req)
if err != nil {
log.Fatal(err)
}
defer res.Body.Close()
if res.StatusCode >= 400 {
log.Fatalf("redrive failed: %d", res.StatusCode)
}
var body payload
if err := json.NewDecoder(res.Body).Decode(&body); err != nil {
log.Fatal(err)
}
// Same event_id — your idempotency key survives the replay.
fmt.Println(body.Delivery.Status, body.RedrivesRemainingToday)
}
{
"delivery": {
"delivery_id": "6f1a2b3c-4d5e-4f60-8718-9a0b1c2d3e4f",
"event_id": "9a1d2c37-4e5f-4a6b-8c9d-0e1f2a3b4c5d",
"event_type": "booking.confirmed",
"status": "pending",
"attempts": 0,
"last_error_code": null,
"last_response_status": null,
"next_attempt_at": "2026-09-07T09:05:00.000Z",
"occurred_at": "2026-08-30T18:42:11.000Z",
"created_at": "2026-08-30T18:42:11.500Z",
"payload_present": true,
"test": false
},
"redrives_remaining_today": 4
}Put a delivery back in the queue
Puts one delivery back at the front of the queue. The body is empty, on purpose: there is no reason field, no delay, and no list — a redrive addresses exactly one delivery, never a batch.
Only a delivered or failed delivery can be replayed. event_id is
unchanged by a redrive, so your idempotency key survives it — a redrive is a
retry, not a duplicate booking notification in your own system. The attempt
series resets and the delivery goes back to pending.
The body is empty: a redrive addresses exactly one delivery, and there is no reason field, no delay and no list, because a redrive is never bulk.
409 conflict when the delivery is already queued, retrying or being sent; when
its body is gone (erased, or past the payload retention window — there is
nothing left to redrive); or when your endpoint is not currently active. detail
names which. Nothing is changed in any of those cases.
429 rate_limited when this credential’s rolling 24-hour redrive allowance is
used up. The allowance is per credential and rolls forward continuously rather
than resetting at midnight, and we do not publish the figure — it is an
operational setting we tune, not a contract.
Live in a sandbox workspace. Returns 501 against a production one.
curl --fail-with-body -X POST "https://maatstays.wtf/v1/webhooks/deliveries/6f1a2b3c-4d5e-4f60-8718-9a0b1c2d3e4f/redrive" \
-H "Authorization: Bearer $MAAT_PARTNER_TOKEN"const res = await fetch(
'https://maatstays.wtf/v1/webhooks/deliveries/6f1a2b3c-4d5e-4f60-8718-9a0b1c2d3e4f/redrive',
{
method: 'POST',
headers: { Authorization: `Bearer ${process.env.MAAT_PARTNER_TOKEN}` },
},
);
const body = await res.json();
if (!res.ok) throw new Error(`${body.code}: ${body.detail}`);
// Same event_id — your idempotency key survives the replay.
console.log(body.delivery.status, body.redrives_remaining_today);
import os
import requests
res = requests.post(
"https://maatstays.wtf/v1/webhooks/deliveries/6f1a2b3c-4d5e-4f60-8718-9a0b1c2d3e4f/redrive",
headers={"Authorization": f"Bearer {os.environ['MAAT_PARTNER_TOKEN']}"},
timeout=30,
)
body = res.json()
if res.status_code >= 400:
raise RuntimeError(f"{body['code']}: {body.get('detail')}")
# Same event_id — your idempotency key survives the replay.
print(body["delivery"]["status"], body["redrives_remaining_today"])
package main
import (
"encoding/json"
"fmt"
"log"
"net/http"
"os"
)
type deliveryView struct {
Status string `json:"status"`
}
type payload struct {
Delivery deliveryView `json:"delivery"`
RedrivesRemainingToday int `json:"redrives_remaining_today"`
}
func main() {
req, _ := http.NewRequest(http.MethodPost,
"https://maatstays.wtf/v1/webhooks/deliveries/6f1a2b3c-4d5e-4f60-8718-9a0b1c2d3e4f/redrive", nil)
req.Header.Set("Authorization", "Bearer "+os.Getenv("MAAT_PARTNER_TOKEN"))
res, err := http.DefaultClient.Do(req)
if err != nil {
log.Fatal(err)
}
defer res.Body.Close()
if res.StatusCode >= 400 {
log.Fatalf("redrive failed: %d", res.StatusCode)
}
var body payload
if err := json.NewDecoder(res.Body).Decode(&body); err != nil {
log.Fatal(err)
}
// Same event_id — your idempotency key survives the replay.
fmt.Println(body.Delivery.Status, body.RedrivesRemainingToday)
}
{
"delivery": {
"delivery_id": "6f1a2b3c-4d5e-4f60-8718-9a0b1c2d3e4f",
"event_id": "9a1d2c37-4e5f-4a6b-8c9d-0e1f2a3b4c5d",
"event_type": "booking.confirmed",
"status": "pending",
"attempts": 0,
"last_error_code": null,
"last_response_status": null,
"next_attempt_at": "2026-09-07T09:05:00.000Z",
"occurred_at": "2026-08-30T18:42:11.000Z",
"created_at": "2026-08-30T18:42:11.500Z",
"payload_present": true,
"test": false
},
"redrives_remaining_today": 4
}Authorizations
A short-lived partner token, obtained by exchanging your API key at
POST /token. Send it as Authorization: Bearer <access_token>.
Tokens live for fifteen minutes by default and never for more than an hour. Mint one per batch of work, not one per request.
The token carries the workspace and the environment its key was issued for. You cannot change either by asking: the exchange ignores everything in the request body for exactly that reason.
It also carries the key's scopes (scope, a list). Every operation names a
scope as x-maat-scope, and a token whose key lacks it is refused with
403 insufficient_scope, naming the scope. Scopes are fixed when a key is
issued and are re-read from the key on every request, so a key narrowed today is
narrowed on its next call — the token's scope is what the key held at exchange
time, for your own reading, never what authorizes a request.
x-maat-scope is the floor, and on the quick-action operations it is not the
whole requirement. PUT and DELETE /listings/{listing_id}/quick-actions/{template_key}
carry rules:write, and additionally require pricing:write when
template_key begins pricing. — the capability depends on the template in
the path, because a cleaning fee changes what a guest pays and a minimum stay
does not. The refusal is the same 403 insufficient_scope, naming
pricing:write. The two READS apply the same requirement without erroring:
GET /listings/{listing_id}/quick-actions still lists the pricing.* rows but
withholds their applied state, and GET /listings/{listing_id}/rules omits the
pricing rules entirely and reports pricing_withheld in warnings. These four
are the only operations in this document whose requirement is not exactly their
x-maat-scope, and each of them says so machine-readably:
x-maat-scope-additional lists the second scope it can require and
x-maat-scope-additional-when says what happens without it.
Never send it as a cookie or in a query string, and never store it — store the API key, mint tokens from it.
Path Parameters
Our id for the delivery, as returned by GET /webhooks/deliveries.
Response
The delivery, put back at pending with attempts reset to zero.
What POST /webhooks/deliveries/{delivery_id}/redrive answers: the delivery,
re-read after being put back in the queue — pending, attempts at zero, the
SAME event_id. That last part is the contract: your idempotency key must
survive a replay, or a redrive becomes a duplicate in your own system rather than
a retry.
One delivery's ENVELOPE — what GET /webhooks/deliveries carries for every row.
No payload, and no field that could hold one: a delivery's body is the most
sensitive thing this platform sends anywhere, so the list carries only whether one
is still held.
Show child attributes
Show child attributes
How many redrives are left in today's allowance, after this one. The allowance is per credential and rolls forward continuously — it does not reset at midnight.