> ## Documentation Index
> Fetch the complete documentation index at: https://run-deliverys.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# Address webhook

> The address-check event sent when a processing check finishes, and how to verify it.

Run sends `ava.verification.completed` to the HTTPS endpoint on your Business. This page is only that event. Shipment events are a different webhook: [Order webhook](https://run-deliverys.mintlify.site/webhooks/orders.md).

You register **one HTTPS endpoint per Business**. Run POSTs a signed JSON envelope to that URL. `data` is the verification, the same object as [Get verification](https://run-deliverys.mintlify.site/address-verification/get-verification.md). It is not a shipment.

## Configure

Set your webhook URL, subscribed event types, and signing secret from the Business Dashboard → **Developers → Setup** (dashboard session — not via API key).

![Run Business Manage Webhook settings](https://mintcdn.com/run-deliverys/n4pOxj_PjcJPxjfQ/images/dashboard/developers-setup.png?fit=max\&auto=format\&n=n4pOxj_PjcJPxjfQ\&q=85\&s=2491596f7c7bbb8e92ee25af1f989ff7)

Developers → Setup — webhook URL, signing secret, and event subscriptions

* On first save, the signing secret is returned **once** (looks like `whsec_••••••••••••••••` when masked) — store the full value securely.
* A company that only has Address Verification starts with `ava.verification.completed`.
* A company that also has Delivery keeps the shipment events and can add this one.
* Use **Rotate secret** if the secret may be compromised. The previous secret remains valid for about 24 hours.

Inspect attempts under **Developers → Webhook deliveries**.

![Webhook delivery attempts table](https://mintcdn.com/run-deliverys/n4pOxj_PjcJPxjfQ/images/dashboard/webhook-deliveries.png?fit=max\&auto=format\&n=n4pOxj_PjcJPxjfQ\&q=85\&s=9da0b809ff48da8550dbb6b3513bd664)

Developers → Webhook deliveries

## When it is sent

`ava.verification.completed` is sent when a processing address check finishes as `confirmed`, `not_confirmed`, or `unreachable`. That includes the close after 5 business days.

It is not sent when you first submit a check, when you accept or decline a field visit, or when a check is confirmed immediately from a past record.

At that same moment Run also sends an email with the subject “Your address check is ready.” and a device push titled “Address check updated.”

Every field on `data` is listed here.

`address.normalized`, `address.lat`, and `address.lng` are present only when `status` is `confirmed`. Otherwise they are `null`. `evidence` is one object, or `null` when Run has not recorded evidence yet.

**id** `string`

Run’s id for this check. It starts with `avr_`.

**external\_ref** `string`

Your reference for this check.

**status** `string`

`confirmed`, `not_found`, `processing`, `not_confirmed`, or `unreachable`. See [Verification statuses](https://run-deliverys.mintlify.site/address-verification/statuses.md).

**created\_at** `string`

When you created the check, in ISO 8601. Null only if the time is missing.

**checked\_at** `string`

When the check finished, in ISO 8601. `null` until then.

**address.input** `string`

The address you sent.

**address.normalized** `string`

The address Run confirmed. `null` unless `status` is `confirmed`.

**address.lat** `number`

Latitude of the confirmed address. `null` unless `status` is `confirmed`.

**address.lng** `number`

Longitude of the confirmed address. `null` unless `status` is `confirmed`.

**evidence** `object`

One evidence object, or `null`. `type` is `delivered_record` for a past delivery, or `location_verification` for a check Run recorded.

**evidence.type** `string`

`delivered_record` or `location_verification`. `null` when `evidence` is `null`.

**evidence.checked\_at** `string`

When this evidence was recorded, in ISO 8601, or `null`.

**evidence.delivered\_at** `string`

When a past delivery arrived, in ISO 8601, or `null`.

**evidence.confirmed\_at** `string`

When the evidence was confirmed, in ISO 8601, or `null`.

**evidence.method** `string`

How it was checked: `field_visit`, `rider`, `agent`, `local_contact`, or `desk_review`. `null` when this evidence has no method.

**evidence.outcome** `string`

`confirmed`, `not_confirmed`, or `unreachable`. `null` when this evidence has no outcome.

**evidence.address\_recorded** `string`

The address that was found, or `null`.

**evidence.area** `string`

The area that was recorded, or `null`.

**evidence.lat** `number`

Latitude recorded with the evidence, or `null`.

**evidence.lng** `number`

Longitude recorded with the evidence, or `null`.

**evidence.reachable** `boolean`

Whether the address could be reached. `null` when that was not recorded.

**evidence.notes** `string`

The note stored with the evidence, or `null`.

**evidence.reference** `string`

A reference stored with the evidence, or `null`.

**evidence.proof\_image\_urls** `string[]`

Photo links. An empty list when there are no photos. Omitted from the object only when `evidence` itself is `null`.

**field\_visit\_offer.available** `boolean`

`true` when you can still accept or decline a field visit.

**field\_visit\_offer.amount\_ngn** `number`

The visit price in naira when `available` is `true`. `null` when no visit is offered. This is the rate on your account.

**field\_visit\_offer.currency** `string`

Always `NGN`.

**notes** `string`

The note for this check, or `null`.

**charge.amount\_ngn** `number`

The amount taken from the wallet, in naira, or `null` when this check has not been charged.

**charge.currency** `string`

Always `NGN`.

**charge.check\_type** `string`

`historical` for a normal check, or `field_visit` for a visit. `null` when this check has not been charged.

**charge.transaction\_id** `string`

The wallet transaction id, or `null` when this check has not been charged.

## Event envelope

Every delivery is a JSON POST with this shape. Headers:

| Header | Description |
| - | - |
| `X-Run-Webhook-Id` | Event id (`evt_…`) |
| `X-Run-Webhook-Timestamp` | Unix seconds (use for verify + replay window) |
| `X-Run-Webhook-Signature` | HMAC-SHA256 hex |
| `X-Run-Delivery-Id` | This delivery attempt id (`dlv_…`) |

## Delivery and retries

Return a **2xx** status to acknowledge receipt.

| Outcome | Behavior |
| - | - |
| Timeouts, connection errors, `408` / `429` / `5xx` | Retried with exponential backoff (+ jitter). `Retry-After` is honored when present |
| Most other `4xx` | Dead-lettered |

Delivery is **at-least-once**. Retries reuse the same event `id` (`evt_…`) — make your handler idempotent. Run does **not** promise global ordering; use `occurred_at` and `data.status`.

In production, webhook URLs must use HTTPS. Run blocks private/reserved destinations and disables redirects when delivering.

## Verify signatures

HMAC-SHA256 of `id + status + timestamp`, keyed with your `whsec_…` secret. Compare the hex digest to `X-Run-Webhook-Signature`. The signature is not a hash of the JSON body.

| Value | Source |
| - | - |
| `id` | `data.id`, the verification id (`avr_…`) |
| `status` | `data.status` (`confirmed`, `not_confirmed`, or `unreachable`) |
| `timestamp` | `X-Run-Webhook-Timestamp` |

Reject timestamps older than about 5 minutes. Always verify before trusting the body.

```http Headers theme={null}
POST <your webhook_url>
Content-Type: application/json
User-Agent: Run-Webhooks/1.0
X-Run-Webhook-Id: evt_01hxyz...
X-Run-Webhook-Timestamp: 1723467600
X-Run-Webhook-Signature: 9f2c...a1b3
X-Run-Delivery-Id: dlv_01hxyz...
```

```javascript Node.js theme={null}
const crypto = require('crypto');

function verify(secret, orderId, status, timestampHeader, signatureHeader, maxAgeSec = 300) {
  const age = Math.abs(Date.now() / 1000 - Number(timestampHeader));
  if (age > maxAgeSec) return false;

  const expected = crypto
    .createHmac('sha256', secret)
    .update(`${orderId}${status}${timestampHeader}`)
    .digest('hex');

  return expected === String(signatureHeader).trim();
}
```

```python Python theme={null}
import hashlib
import hmac
import time


def verify(secret, order_id, status, timestamp, signature_header, max_age_sec=300):
    if abs(time.time() - int(timestamp)) > max_age_sec:
        return False
    expected = hmac.new(
        secret.encode(),
        f"{order_id}{status}{timestamp}".encode(),
        hashlib.sha256,
    ).hexdigest()
    return hmac.compare_digest(expected, signature_header.strip())
```

```php PHP theme={null}
function verify_run_webhook(
  string $secret,
  string $orderId,
  string $status,
  string $timestamp,
  string $signatureHeader,
  int $maxAgeSec = 300
): bool {
  if (abs(time() - (int) $timestamp) > $maxAgeSec) {
    return false;
  }
  $expected = hash_hmac('sha256', $orderId . $status . $timestamp, $secret);
  return hash_equals($expected, trim($signatureHeader));
}
```

```go Go theme={null}
package main

import (
  "crypto/hmac"
  "crypto/sha256"
  "encoding/hex"
  "math"
  "strconv"
  "strings"
  "time"
)

func verify(secret, orderId, status, timestamp, signatureHeader string, maxAgeSec int64) bool {
  ts, err := strconv.ParseInt(timestamp, 10, 64)
  if err != nil {
    return false
  }
  if int64(math.Abs(float64(time.Now().Unix()-ts))) > maxAgeSec {
    return false
  }
  mac := hmac.New(sha256.New, []byte(secret))
  mac.Write([]byte(orderId + status + timestamp))
  return hex.EncodeToString(mac.Sum(nil)) == strings.TrimSpace(signatureHeader)
}
```

```json Confirmed theme={null}
{
  "id": "evt_01hxyzaddress",
  "type": "ava.verification.completed",
  "api_version": "2026-08-01",
  "created_at": "2026-10-26T11:00:00.000Z",
  "occurred_at": "2026-10-26T11:00:00.000Z",
  "data": {
    "id": "avr_01hxyzaddresscheck",
    "external_ref": "ORDER-1842",
    "status": "confirmed",
    "created_at": "2026-10-25T15:20:00.000Z",
    "checked_at": "2026-10-26T11:00:00.000Z",
    "address": {
      "input": "12 Adeola Odeku, Victoria Island, Lagos",
      "normalized": "12 Adeola Odeku Street, Victoria Island, Lagos",
      "lat": 6.4281,
      "lng": 3.4219
    },
    "evidence": {
      "type": "location_verification",
      "checked_at": "2026-10-26T11:00:00.000Z",
      "delivered_at": null,
      "confirmed_at": null,
      "method": "desk_review",
      "outcome": "confirmed",
      "address_recorded": "12 Adeola Odeku Street, Victoria Island, Lagos",
      "area": "Victoria Island",
      "lat": 6.4281,
      "lng": 3.4219,
      "reachable": true,
      "notes": null,
      "reference": null,
      "proof_image_urls": []
    },
    "field_visit_offer": {
      "available": false,
      "amount_ngn": null,
      "currency": "NGN"
    },
    "notes": null,
    "charge": {
      "amount_ngn": 2000,
      "currency": "NGN",
      "check_type": "historical",
      "transaction_id": "txn_01hxyz"
    }
  }
}
```

```json Not confirmed theme={null}
{
  "id": "evt_01hxyzaddress",
  "type": "ava.verification.completed",
  "api_version": "2026-08-01",
  "created_at": "2026-11-01T15:20:00.000Z",
  "occurred_at": "2026-11-01T15:20:00.000Z",
  "data": {
    "id": "avr_01hxyzaddresscheck",
    "external_ref": "ORDER-1842",
    "status": "not_confirmed",
    "created_at": "2026-10-25T15:20:00.000Z",
    "checked_at": "2026-11-01T15:20:00.000Z",
    "address": {
      "input": "44 Unity Avenue, Okerube, Lagos",
      "normalized": null,
      "lat": null,
      "lng": null
    },
    "evidence": {
      "type": "location_verification",
      "checked_at": "2026-11-01T15:20:00.000Z",
      "delivered_at": null,
      "confirmed_at": null,
      "method": "desk_review",
      "outcome": "not_confirmed",
      "address_recorded": "44 Unity Avenue, Okerube, Lagos",
      "area": null,
      "lat": null,
      "lng": null,
      "reachable": null,
      "notes": null,
      "reference": null,
      "proof_image_urls": []
    },
    "field_visit_offer": {
      "available": false,
      "amount_ngn": null,
      "currency": "NGN"
    },
    "notes": null,
    "charge": {
      "amount_ngn": null,
      "currency": "NGN",
      "check_type": null,
      "transaction_id": null
    }
  }
}
```

```json Unreachable theme={null}
{
  "id": "evt_01hxyzaddress",
  "type": "ava.verification.completed",
  "api_version": "2026-08-01",
  "created_at": "2026-10-26T11:00:00.000Z",
  "occurred_at": "2026-10-26T11:00:00.000Z",
  "data": {
    "id": "avr_01hxyzaddresscheck",
    "external_ref": "ORDER-1842",
    "status": "unreachable",
    "created_at": "2026-10-25T15:20:00.000Z",
    "checked_at": "2026-10-26T11:00:00.000Z",
    "address": {
      "input": "44 Unity Avenue, Okerube, Lagos",
      "normalized": null,
      "lat": null,
      "lng": null
    },
    "evidence": {
      "type": "location_verification",
      "checked_at": "2026-10-26T11:00:00.000Z",
      "delivered_at": null,
      "confirmed_at": null,
      "method": "field_visit",
      "outcome": "unreachable",
      "address_recorded": "44 Unity Avenue, Okerube, Lagos",
      "area": "Okerube",
      "lat": 6.6012,
      "lng": 3.3511,
      "reachable": false,
      "notes": "The address was not there.",
      "reference": null,
      "proof_image_urls": [
        "https://res.cloudinary.com/example/image/upload/street.jpg"
      ]
    },
    "field_visit_offer": {
      "available": false,
      "amount_ngn": null,
      "currency": "NGN"
    },
    "notes": "The address was not there.",
    "charge": {
      "amount_ngn": 8000,
      "currency": "NGN",
      "check_type": "field_visit",
      "transaction_id": "txn_01hxyz"
    }
  }
}
```


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.