> ## Documentation Index
> Fetch the complete documentation index at: https://docs.orbit.devotel.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Troubleshooting: Apple Messages for Business onboarding and send failures

> Triage the four AMB failure surfaces: an agent stuck in pending while Apple clears it, the 422 that names a missing listPickerJson, formJson, or timePickerJson metadata key, capability-gated rich payloads and Apple Pay rejects, and the HMAC secret at onboarding.

# Troubleshooting: Apple Messages for Business onboarding and send failures

Apple Messages for Business (AMB) failures split into two moments: onboarding (the agent never reaches `approved`, or the webhook secret no longer verifies) and sending (a `422` naming a missing `*Json` metadata key, or a capability the agent does not hold). Both are deterministic, and neither is fixed by resending the same request. This page walks the failure surfaces the [Apple Messages for Business channel page](/channels/apple-messages) exposes, in triage order.

## Symptom map

| Symptom | Cause | Fix |
| - | - | - |
| Agent stays `status: "pending"` for days | Apple-side clearance has not landed; Orbit only mirrors it | Keep polling with backoff; check Apple Business Register for an open action; escalate with the agent ID |
| Agent shows `status: "suspended"` | Apple took the agent down after it was approved | Repair or re-register before the next send; treat it as out of the routing set |
| `422` on a send naming `listPickerJson`, `formJson`, or `timePickerJson` | The interactive send omitted the `*Json` metadata key matching its payload | Add the serialized payload under the named key and resend; nothing queues |
| Rich payload rejected or dropped | The agent lacks the `interactive`, `form`, or `timePicker` capability | Enable the capability on the agent, or send plain text |
| Native Apple Pay expected, hosted link returned | The agent lacks `applePay`, or the merchant identifier is not registered | Set `applePayEnabled` and `merchantIdentifier`, or send the hosted link |
| Inbound events fail signature verification | The stored HMAC secret no longer matches Apple's current one | Rotate the secret per the onboarding guide |

## Agent stuck in pending — Apple-side clearance has not landed

A fresh registration returns `201` with `status: "pending"`. Orbit cannot speed up Apple's review: the agent flips to `approved` only when Apple clears the business on their side. The channel page's polling pattern (re-fetch `GET /api/v1/channels/amb/agents/{id}` with exponential backoff) is the correct way to wait. Do not route live conversations to a `pending` agent.

When the status never flips:

1. Check Apple Business Register for an open action on the business entry. A missing document or a failed review keeps the agent `pending` no matter what Orbit does.
2. Confirm the `business_id` you passed at create time is the approved identity, not a draft or sandbox one.
3. Keep polling with backoff. No retry from your side moves the status.
4. If the register shows the business as approved but the agent still reads `pending`, escalate: the MSP-side handoff is the remaining leg, and support can check it with your agent ID and `business_id`.

### Suspended means repair or re-register

`suspended` is not a pause. Apple took the agent down after approval, for a policy or registration issue on their side. Treat any status regression as out-of-routing-set: resolve the cause in Apple Business Register, then repair the agent or re-register it before the next send. The onboarding guide's polling loop throws on `suspended` for exactly this reason.

## 422 on a send: the missing \*Json metadata key

An interactive send is a normal unified send with `channel: "amb"` and `msgType: "interactive"`; the Apple payload travels as a serialized JSON string in the matching metadata key: `listPickerJson` for list pickers, `formJson` for forms, `timePickerJson` for time pickers. A send missing the matching `*Json` metadata key is rejected with `422` naming the key. Fix the template and resend; nothing queues.

Full-error sample, from a list-picker send that carried `msgType: "interactive"` but no `listPickerJson`:

```json theme={null}
{
  "error": {
    "code": "VALIDATION_ERROR",
    "message": "amb interactive send requires listPickerJson in metadata"
  },
  "meta": {
    "request_id": "req_amb_4f2c",
    "timestamp": "2026-09-30T09:14:00Z"
  }
}
```

Resolution:

1. Read the key name off the rejection. It is exactly the key your send omitted.
2. Add the serialized payload under that key. The value is a JSON string, not a nested object.
3. Confirm the agent holds the matching capability (see the next section). A correct key on an agent without the capability fails the next gate.
4. Resend. The reject is deterministic; the corrected body passes on the first retry.

## Capability-gated rejects

Rich interactions and Apple Pay are agent-specific. An agent without `interactive` cannot carry a list picker; without `form` no form renders; without `timePicker` no timeslot sheet. Native Apple Pay needs both the `applePay` capability on the agent and a registered `merchantIdentifier`. Unsupported or malformed Apple payloads are rejected rather than rendered by another channel.

Two shapes show up in practice:

* **Rich payload on an agent without the capability.** The send is rejected or the payload is dropped. Read the agent record's `capabilities`, enable the one the payload needs, or fall back to plain text for that conversation.
* **Native Apple Pay without the merchant identifier.** The checkout resolver degrades by design: with `applePayEnabled` and `merchantIdentifier` both set it returns `method: "native"` and the Apple Pay sheet payload; otherwise it returns `method: "hosted"` with `native` null. If you expected a native sheet and received a hosted link, the merchant identifier is the gap. Register it with Apple and set it in the checkout config, or keep sending `hosted.url` into the thread. Both paths reconcile against the same `paymentRequest`, so your order logic does not fork. The [commerce guide](/guides/apple-messages-commerce-checkout) covers the decline-and-retry loop.

## The HMAC secret at onboarding

The base64 HMAC `secret_key` you pass at agent creation authenticates the events Apple delivers to your AMB webhook. Orbit encrypts the key at rest and only exposes whether a secret is stored; it never returns the value.

* **Stored indicator set, events verifying.** Nothing to do. This is the healthy state.
* **Secret lost on your side.** You cannot read it back out of Orbit. Issue a new key in Apple Business Register and update the agent with the onboarding guide's rotation steps. Until the stored secret matches Apple's current one, inbound events fail verification.
* **Secret changed at Apple.** Update the agent before the old key stops working. Rotating or deleting an agent changes webhook verification and routing for that Apple business.

## Decision checklist

1. Onboarding or send? `pending`/`suspended` and secret problems are onboarding; `422` and capability rejects are send-side.
2. Onboarding: read the agent `status`. `pending` means wait on Apple and check the register; `suspended` means repair or re-register, with no sends in between.
3. Send-side `422`: read the key the rejection names, fix the template, resend once. Never retry the same body.
4. Capability reject: compare the payload against the agent's `capabilities`; for native pay, confirm the `merchantIdentifier` is registered and set.
5. Webhook-side: check the stored-secret indicator; rotate when the secret changed at Apple.

## What not to try

* **Do not send live traffic to a `pending` agent.** Apple has not cleared it; the status will not move from your side.
* **Do not retry a `422` that names a `*Json` key.** The reject is deterministic and nothing queues; the same body fails identically.
* **Do not move an Apple payload to another channel to dodge a capability reject.** Apple payloads are not interchangeable with WhatsApp, RCS, or web-chat payloads; they are rejected rather than rendered elsewhere.
* **Do not expect Orbit to return your HMAC secret.** Only the stored indicator is exposed; rotate instead.
* **Do not treat `suspended` as transient.** It does not clear by itself; repair or re-register.

## When to escalate

Open a support ticket when the register shows the business approved but the agent still reads `pending` after several days, when a corrected template still returns the same `422` naming the same key, or when native Apple Pay is configured with a registered merchant identifier but the resolver still returns `hosted`. Include:

* The agent ID (`data.id` from the create response), not just the Apple `business_id`.
* One `meta.request_id` from the rejection, for send-side failures.
* The agent's `capabilities` array and, for pay failures, the `merchantIdentifier` you configured.

## See also

* [Apple Messages for Business](/channels/apple-messages) — the channel page this runbook triages.
* [Apple Messages for Business onboarding](/guides/apple-messages-for-business-onboarding) — the wizard, request bodies, status polling, and secret rotation.
* [AMB commerce: interactive flows and Apple Pay checkout](/guides/apple-messages-commerce-checkout) — payloads, cart merge, token capture, and reconciliation.
* [KakaoTalk template gates](/troubleshooting/kakao-template-gates) — the comparable deterministic-422 runbook for another approval-gated channel.


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