Skip to main content

Troubleshooting: RCS BRAND_NOT_APPROVED (409)

Your RCS request failed with HTTP 409 and the error code BRAND_NOT_APPROVED. Bots can only register under a brand that is not under a carrier-platform hold, so any request that names a rejected or suspended brand stops at this gate. The response is not a bug — it is the one thing standing between your bot and a brand the reviewer refused.
Approval, refusal, and takedown decisions belong to the review workflow — Devotel operations plus Dotgo, the RCS carrier-directory partner. Approval is a manual workflow by design: someone reviews the brand before a bot can go live, and a refusal lands as this 409 rather than as a failed send downstream. Do not program around it; fix the brand.

What brand approval is

Every RCS bot (the agent subscribers see in their messaging app) links to a brand — the verified business entity carriers screen before they let an agent launch. Brand registration is a manual review workflow:
  1. You create the brand (POST /api/v1/rcs/brands) with its identity and KYC fields and submit it (POST /api/v1/rcs/brands/:id/submit).
  2. The brand enters the review queue and moves to pending_review. Devotel operations and Dotgo review the record — the 409 you are reading about exists precisely so that a refused brand cannot slip a bot past the directory. Because the review is human, lead times here are expectations, not guarantees.
  3. A reviewer decision lands as one of the statuses below. The brand record’s status field and, when a submit was refused, rejection_reason tell you which.
A bot submit names a brand id, and the server checks that brand before it forwards anything upstream. Only two statuses are a hard stop:
  • rejected — Devotel ops refused the brand, or Dotgo’s own screening refused it and the verdict was mirrored down. This is a refused-for-cause state, not a queue position.
  • suspended — a platform takedown: the brand was live and was pulled. Treat it as a hold on all new bots under that brand until reinstated.
Every other status — draft, pending_review, submitted_to_dotgo, approved — passes the gate. A brand still in review does not 409; Dotgo runs its own screening when the bot is submitted alongside the brand, so a brand-new tenant reaching RCS onboarding is never blocked by a gate whose verdict a human has not even written yet. The RCS onboarding guide covers the full create → submit → verify → launch ordering; this page picks up at “onboarding stopped on a 409.”

Where the 409 surfaces

Three RCS calls run the same brand-not-held precondition before they do any work, and each returns the same BRAND_NOT_APPROVED body: Response shape (all three):
The PUT /api/v1/rcs/bots update path ships an update-brand trigger on the same precondition — an edit that re-points a bot at a held brand 409s the same way a create would, so a 409 on update is not a separate bug class.

Read details.brand_status

Every BRAND_NOT_APPROVED body carries the two facts needed to act, without a second API call:
  • details.brand_id — which brand is blocking. Keep it for the resubmit loop and for a support ticket.
  • details.brand_status — what to do next:
    • waiting for review (draft / pending_review / submitted_to_dotgo) → no fix is needed. The brand is still moving through review and the bot gate already passed — re-read the symptom; something later in the flow failed, not the brand gate.
    • rejected → the brand was refused-for-cause. Fix the record and resubmit (below).
    • suspended → platform takedown. Fix the record and resubmit; treat a takedown as higher-severity than a refusal and reach out if the reason is not self-evident.

The resubmit loop — correct the brand, then resubmit

A refused or suspended brand is editable precisely so you can correct what the reviewer called out. draft and rejected are the only two statuses that accept edits, so the loop is short and intentional:
  1. Read the verdict. GET /api/v1/rcs/brands/:id returns the full record. On a refusal the reviewer-populated rejection_reason names the item to fix — a thin contact block, a missing legal entity, a mismatched website. Read it literally.
  2. Edit the brand. PATCH /api/v1/rcs/brands/:id accepts the corrected fields (every field is optional; send only the ones you are changing). Complete the KYC block — contact name, contact email or phone, postal address — and complete tax_id and legal_entity_name when you have them; thin forms are the normal refusal class. The RCS brand verification KYC walkthrough maps which documents a country’s carriers expect.
  3. Resubmit the brand. Call POST /api/v1/rcs/brands/:id/submit again. A rejected brand re-enters review with the corrected record; a suspended brand moves back into review for a takedown appeal.
  4. Retry the bot call. Once the brand clears review, re-send the bot submit / update / verify that 409’d.
Do not re-fire the same bot payload against a held brand. The 409 is the gate doing its job; an unchanged bot body only earns the same refusal, faster, and re-queues nothing. The thing to resubmit is the brand, not the bot.
A rejected brand that you decide not to fix can be deleted (DELETE /api/v1/rcs/brands/:id) only while it sits in draft or rejected. Bots linked to that brand stay in place and unlinked; re-link them to a held-free brand before you resubmit them.

What not to do

  • Do not treat waiting for review as a yes. A brand still in review passes the gate, so Dotgo’s screening gets to run — but a refusal there is the same BRAND_NOT_APPROVED once the verdict lands as rejected.
  • Do not route around the gate by re-creating the brand. A second brand with the same refused records earns the same refusal on its single shared review path; edit the existing rejected brand instead. Deleting and re-creating also strands the bot rows linked to the old brand id.
  • Do not poll the bot call for a flip. The gate responds to the brand record, so the poll belongs on GET /api/v1/rcs/brands/:id status, not on the bot endpoint.

When to escalate

Escalate to support@devotel.io when the reviewer’s rejection_reason does not map to anything you can correct, when a suspended brand needs a takedown appeal, or when the same correction has been refused twice. Include:
  1. Your tenant ID (dashboard → Settings → Organization, or organizationId on GET /api/v1/me).
  2. The details.brand_id from the 409 body.
  3. The verbatim details.brand_status value and, when present, the brand’s rejection_reason.

See also