BellD / DevelopersDocumentation · latest

CLI behavior

Options and output

--json, --verbose and --force are booleans. Other options take a value. The generated CLI reference lists the options accepted by each command and the seven globals. --force is local to init. Unknown options or missing positional arguments are usage errors.

--json preserves the API object, with centralized secret redaction. Human errors go to stderr; JSON problems go to stdout. Inspect business state even after exit 0.

belld revision confirm accepts --edits JSON to override detected descriptor fields before confirming. Accepted top-level fields are build_plan, runtime, entrypoint and ports; the API validates their values. Without --edits the detected descriptor is confirmed as-is.

belld revision confirm rev_REPLACE --product prod_REPLACE --edits '{"ports":[{"port":8080,"protocol":"http"}]}'

Deploy to production with belld deploy rel_REPLACE --environment production. A production deployment is public: anyone with the address can open the App, while a staging App on BellD opens only for signed-in workspace members. Only workspace owners and admins can deploy to production.

Exit codes

0: HTTP operation succeeded. 1: general error, 429, 5xx, or timeout. 2: usage or option error. 3: unauthenticated or missing credentials. 4: not found. 5: forbidden.

Polling and retries

While belld scan and belld evaluate wait for a pipeline run, they print a progress line to stderr each time the run's phase or a stage's status changes: phase PHASE or stage STAGE STATUS. Nothing is printed when live status is not available. Progress lines never go to stdout, including with --json, and do not affect the exit code.

The default deadline is 600 seconds. Backoff is 0.2, 0.4, 0.8, 1.6, then 2 seconds, including HTTP wait in the deadline. Service creation polls source freeze. Evaluation stops at qualification qualified or stopped, or pipeline completed, passed, warning, blocked, failed, or unsafe. Check qualification before releasing.

Descriptor confirmation and evaluation retry admission not_found within the deadline with the same Idempotency-Key. After evaluation is accepted, polling uses pipeline GET. Other POST operations do not retry automatically. Deployment, Surface, and pipeline-run GET commands read once.

When using local credentials obtained through belld login with a refresh token stored on disk, the internal polling in belld evaluate automatically renews the access token on HTTP 401 and retries the request once. External tokens supplied through BELLD_TOKEN or --token have no refresh token, so HTTP 401 still ends the command with exit code 3. Exit 3 is not an evaluation failure: the pipeline run continues on the platform, so use belld pipeline-run get pipe_REPLACE --json repeatedly until the run reaches a terminal state. If an external token has expired, obtain a fresh token and update BELLD_TOKEN or --token, or remove the external token and run belld login to use local refresh credentials.

Each invocation normally uses a fresh key; --idempotency-key KEY supports deliberate replay of the same operation across calls. Keys are 1–256 printable non-space ASCII characters.

When a write fails in human-readable mode, the CLI prints the key it used on stderr as idempotency-key KEY; rerun the same command with --idempotency-key KEY to replay it without creating a duplicate. JSON mode output is unchanged; pass --idempotency-key yourself if you need to replay.

Credentials and pagination

Use login or BELLD_TOKEN; a flag token overrides the environment. Do not put provider secrets in arguments. Credential creation accepts stdin or --from-env NAME. CLI output redacts secrets; never assume an arbitrary external log is safe to publish.

belld binding create requires --capability-scope JSON as one structured JSON argument. Its id must match --capability; the Broker validates the capability-specific shape before creating a new binding. Missing or malformed JSON is a local usage error (exit 2) and sends no HTTP requests. For example:

belld binding create --environment production --credential cred_REPLACE --capability ai.generate --actions generate --scope model-a --expires-at 2030-01-01T00:00:00Z --capability-scope '{"id":"ai.generate","scope":{"models":["text"],"max_tokens_per_call":1000},"provider_models":{"text":"model-a"}}'

Add --app app_ID to limit the binding to one App in the same workspace; the App must be active. Without --app the binding is shared across the workspace. Once an App has its own active or blocked binding for a capability and environment, its services use only the App's bindings and no longer fall back to shared ones.

For ai.generate, set --provider on belld credential create to one of the names below. The secret is your own API key from that provider. The provider bills you for these calls, and BellD does not charge Bell Credits for them. In provider_models, use the provider's own model names.

openai_compatible: OpenAI. Get the key from the OpenAI Platform.

gemini: Google Gemini API. Get the key from Google AI Studio.

deepseek: DeepSeek. Get the key from the DeepSeek Platform.

rap: NCHC Taiwan AI RAP. Get the key from the NCHC portal (portal.genai.nchc.org.tw).

To use BellD's own AI account instead of your own key, set "credential_owner": "platform" on ai.generate in your capability declaration. You do not create a credential or a binding. Each call is charged in Bell Credits to your workspace, including Free workspaces, at BellD's rate for the model. Requests are sent to the provider through BellD's account. If max_tokens_per_call in your declaration is above BellD's limit, calls are refused with binding_missing. Only text input sent inline in the call is accepted: evidence references, images, audio, files, n other than 1, and options BellD does not support on its account (for example service_tier or web_search_options) are refused with outside_contract.

For email.send, set --provider on belld credential create to one of the names below. The secret is your own key from that provider. The provider bills you for these emails, and BellD does not charge Bell Credits for them. If your capability contract lists providers in provider.allow, include the one you use.

resend: Resend. Get the API key from the Resend dashboard.

postmark: Postmark. Use the server token of one of your Postmark servers. Postmark only sends from an address you have confirmed as a sender signature in Postmark.

email.send is available on paid plans only; on the Free plan the call is refused. In each call, recipients must list every address in to, cc and bcc exactly once, in lowercase, and bytes must be at least the size of the subject, text, HTML and attachments. A binding for email.send limits the sender addresses and recipient domains, for example:

belld binding create --environment production --credential cred_REPLACE --capability email.send --actions send --expires-at 2030-01-01T00:00:00Z --capability-scope '{"id":"email.send","scope":{"from_addresses":["hello@example.com"],"recipient_domains":["*"],"max_recipients":10,"max_message_bytes":262144}}'

To send from BellD's own email account instead of your own provider, set "credential_owner": "platform" on email.send in your capability declaration and use no-reply@apps.mail.belld.ai as the only entry in from_addresses. This is available on paid plans only. Each recipient is charged in Bell Credits to your workspace at BellD's rate. The sender name is your app's name; if the name has characters that are not allowed, or contains "BellD", the email is sent without a sender name. You can set reply_to to your own address. Custom headers and evidence references are not accepted; send the content inline.

To store objects in BellD's storage instead of your own bucket, declare storage.object with "credential_owner": "platform". Each object can be up to 1 MiB. Stored data is billed at 4 Bell Credits per GiB-day and counts toward your workspace's storage limit together with KV: 1 GiB on the Free plan, 20 GiB on paid plans. Staging, production and previews each use a separate area; all previews of a service share one area. Deleted or overwritten objects cannot be recovered.

For ai.generate, input is the body of an OpenAI-style chat completions request, such as { "messages": [...] }. BellD sets the model from the binding and sets the token limit from max_tokens in your call. On success, the result is the reply text as a string. For data.kv, a set call must pass bytes equal to the size of the value in bytes; otherwise the call is refused.

List cursors are opaque. Pass --limit (1–200), --cursor, and --order asc or desc only where the command table allows them. Budget list has no pagination options.