Merged
Conversation
Step 1 of Build CLI improvements. The JWT already exposes whether a credential belongs to a Bandwidth Build account (express: true) and which roles it carries; today login parses these and discards them. Persist them on the active profile so subsequent commands can render capability hints, gate Build-restricted operations locally, and produce Build-aware error messages. No user-visible behavior change in this commit — plumbing only.
The login flow's guidance for credentials without an account binding echoed an internal field value back to the user. Replace it with a direct, descriptive message that gives the same actionable advice without surfacing implementation details. Tighten the AGENTS.md section that described the same flow.
Human output adds a Type and "Capable of" line for Bandwidth Build accounts so users immediately see that voice-only commands work and messaging/numbers/etc. don't. Full accounts see the same output as before — no new noise. --plain now emits structured JSON (it previously printed the same human text, breaking any agent or script that took the flag at its word). The shape includes express, roles, and a derived capabilities map for branching logic. Capabilities() is a pure role-to-feature mapping with unit tests covering Build, no-roles, messaging-capable, and campaign+TFV cases.
The CLI's 403 paths today are inconsistent: some commands wrap the error with a tailored message, others let the raw "API error 403: Forbidden" through. Exit codes are split between 1, 2, and 4 for morally-equivalent failures, so an agent can't reliably branch on "feature limit vs. real auth failure." Introduce a typed FeatureLimitError in cmdutil that maps to ExitConflict (4), and route every 403-on-feature-gate path through it: vcp/list, site/list, number/search, number/list, tendlc/*, tfv get, tfv submit, shortcode/list. True auth failures (token expired, bad credentials) keep returning a raw APIError that maps to ExitAuth (2) — so an agent can still tell the two apart. When the active profile is an express account, Wrap403 produces a plan-aware message pointing the user at the upgrade path. Otherwise it points at the role to request. number/list keeps its richer endpoint-specific text (the pre-provisioned number is reachable via the account portal) but is now parameterized on isBuild for deterministic testing. Tests cover both branches plus the underlying-error preservation contract that ExitCodeForError relies on.
Two new exit-code mappings: * HTTP 402 (Payment Required) — out of credits, declined card, or no payment method on file. Maps to ExitConflict (4) — non-retryable; the user must act before the CLI can succeed. * HTTP 429 (Too Many Requests) — transient quota or rate-limit failure. Mapped to a new ExitRateLimit (7) so an agent can branch on "back off and retry" rather than treating it as a generic error. Previously both fell through to ExitGeneral (1), which conflated billing/quota issues with bad input or unexpected failures and gave agents no way to react differently. Tests cover the full mapping matrix including FeatureLimitError precedence and wrapped errors.
Build accounts ship with a pre-provisioned voice application whose callback URL points at a Bandwidth-hosted sample endpoint. The existing CheckCallbackURL preflight uses isPlaceholderURL, which treats anything containing "bandwidth.com" as fake — so the sample callback gets flagged and the user is told to fix something that isn't the actual problem. The actual problem is that Build accounts are voice-only and can't send messages at all. Detect that case before the preflight runs and return a FeatureLimitError pointing at the upgrade path. Failure now exits 4 (consistent with other Build feature gates) rather than 1 with misleading guidance.
Profile and status now expose the field as Build / "build", matching how the product is named in user-facing material. Helper renames to match: ActiveBuild() in cmdutil. The JWT struct tag still maps the server's field name to the new Go field, isolating that detail to a single line of translation at the auth boundary. The Go field rename lands in a separate commit from the doc updates that depend on it.
The CLI now detects Build accounts at login, surfaces the account type and capabilities through auth status, and routes Build-gated failures through a consistent exit code with tailored messages. The user-facing docs need to point at those signals so humans and agents know how to read them. README: - Replace the vague "usage limits and terms apply" callout with a concrete one: which commands work, which fail with exit 4, and a link to dev.bandwidth.com for current pricing/credit/limit detail rather than enumerating numbers that will rot. - Drop the broken "run band number list" instruction — number list isn't supported on Build yet. - Exit codes table: expand 4 to cover feature limits and payment required, add 7 for rate limiting. AGENTS.md: - New "Account Type and Capabilities" subsection under Authentication describing the build flag and capabilities map in auth status JSON, so agents can branch on plan limits before attempting work. - Same fix to the "Build Registration" workflow's number-list line. - Exit codes table: rename 2 to "Auth error" (true 401), expand 4 to cover plan/role/feature gates and 402, add 7 for 429. - Error patterns table: add rows for the new Build-aware messages, 402, and 429. - Limitations section: add a Build account entry calling out the feature scope, the runtime limits that aren't statically detectable (verified-number-only on Free Trial, 30-min cap, 5-concurrent), and the exit-code mapping for billing/quota errors. No new content duplicates the FAQ — current pricing, credit costs, and trial limits live at dev.bandwidth.com so they don't go stale in this repo.
✅ Snyk checks have passed. No issues have been found so far.
💻 Catch issues earlier using the plugins for VS Code, JetBrains IDEs, Visual Studio, and Eclipse. |
michaela-band
previously approved these changes
Apr 29, 2026
- Rename TestCapabilities case "messaging-capable" → "messaging and voice" (per @brianluisgomez): the case sets both Messaging and HttpVoice roles, so the name should reflect both. Matches the style of the adjacent "campaign and tfv" case. - Tighten the README "What you get" line (per @michaela-band): add a "then" for sentence flow before the make-a-call link, and refer to the "Bandwidth App" rather than the "Bandwidth account portal" (the canonical product name).
ckoegel
approved these changes
Apr 30, 2026
joshraub-bw
approved these changes
Apr 30, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Makes the CLI aware of Bandwidth Build accounts so users (and agents) get clear, actionable signals when they hit a Build feature limit, instead of misleading "API error 403" or "no working callback URL" errors. Builds purely on the JWT — no new API surface exposed.
Eight commits, each independently reviewable. No breaking changes for non-Build accounts (verified end-to-end against a full Bandwidth account during development).
Test plan