# BOLD API, complete reference > One front door for every BOLD Studios product, metered against one balance in cents. This file is the entire documentation set: every operation, every error, every header, and working code. Nothing else needs fetching. Base URL: https://boldstudios.io/api/v1 Generated: 2026-10-16T17:58:00.912Z from the live catalog. 203 operations. Other formats of this same catalog: - `GET https://boldstudios.io/api/v1` JSON index with live prices - `https://boldstudios.io/api/v1/openapi.json` OpenAPI 3.1 - `https://boldstudios.io/api/v1/llms.txt` the short index - `https://boldstudios.io/api/v1/AGENTS.md` drop-in instructions for a coding agent - `https://boldstudios.io/api/v1/mcp` MCP server, same auth, same prices - `https://boldstudios.io/api/v1/collection.json` Postman collection, every operation - `https://boldstudios.io/api/v1/docs/webhooks.md` the webhook receiver contract - https://boldstudios.io/developers for a person Placeholders in this documentation are written as ``, for example `bold_live__`. Ids in worked examples are well formed but fictional: they are the right shape to paste into a client and the wrong value to send to the API, which will answer 404 rather than touching anything real. ## Contents - Authentication - Money - Rate limits - Pagination - Idempotency, exactly - Versioning and deprecation - Test keys and live keys - Limits and conventions - Response headers - Errors - Quickstart: curl, TypeScript, Python - MCP - Privacy - Operations ## Authentication ``` Authorization: Bearer bold_live__ ``` Keys are minted at https://boldstudios.io/developers and shown exactly once. There is no way to read one back, so if it is lost, mint another and revoke the old one. A key carries scopes, an optional expiry, an optional daily spend cap, and its own rate limits. Prefer a narrow scope for anything that ships: a leaked key with `ai:chat` cannot touch your documents. Scopes in use today: - `ai:chat`: ai.chat - `ai:images`: ai.generate_image - `ai:transcribe`: ai.transcribe, ai.transcript - `artist:read`: artist.list_releases, artist.get_release, artist.list_links, artist.royalty_coverage - `calendar:read`: calendar.list, calendar.list_events, calendar.slots, calendar.list_bookings - `calendar:write`: calendar.create, calendar.create_event, calendar.book, calendar.cancel_booking - `campaign:read`: campaign.list, campaign.get - `clip:read`: clip.list_projects, clip.list_sources, clip.list_clips, clip.get_clip - `crm:read`: crm.list_workspaces, crm.search_contacts, crm.get_contact, crm.list_segments, crm.segment_members, crm.followups - `crm:write`: crm.upsert_contact, crm.add_note - `embed:read`: embed.list, embed.stats - `embed:write`: embed.create, embed.delete - `mail:read`: mail.list_mailboxes, mail.list_messages, mail.get_message - `mail:write`: mail.create_mailbox, mail.send, mail.mark - `monitor:admin`: monitor.status - `network:read`: network.list, network.guide, network.now, network.asrun, network.audience, network.list_bookings, network.rundown - `network:write`: network.create_slot, network.cancel_slot, network.log_asrun, network.book_guest, network.update_booking - `ops:read`: ops.list_builds, ops.get_build, ops.list_tasks, ops.list_runs, ops.ledger, ops.list_playbooks, ops.get_session, ops.list_sessions - `ops:write`: ops.create_brief, ops.save_blueprint, ops.import_tasks, ops.approve_blueprint, ops.fork_playbook, ops.start_session, ops.steer, ops.stop, ops.answer, ops.approve - `promote:read`: promote.plan, promote.console, promote.report - `promote:write`: promote.create_campaign - `publish:read`: publish.list_posts, publish.analytics, publish.list_destinations - `publish:write`: publish.create_destination, publish.rotate_destination, publish.delete_destination - `qa:read`: qa.get, qa.list - `qa:write`: qa.run - `qr:read`: qr.list, qr.stats - `qr:write`: qr.create - `refer:read`: refer.partner_summary, refer.payouts_due, refer.stats - `refer:write`: refer.upsert_program, refer.upsert_partner, refer.conversion - `royalties:read`: royalties.balances, royalties.list_releases, royalties.my_statement - `royalties:write`: royalties.import_statement, royalties.set_shares, royalties.record_payment - `send:read`: send.list_brands, send.list_domains, send.list_contacts, send.list_lists, send.list_suppressions, send.list_campaigns, send.get_email, send.domain_health - `send:write`: send.upsert_contact, send.delete_contact, send.create_list, send.update_list_members, send.suppress, send.create_campaign, send.sync_domains - `sign:read`: sign.list_documents, sign.get_document, sign.audit_trail - `sign:write`: sign.create_document, sign.void_document - `site:read`: site.list_projects, site.get_content, site.versions, site.status - `site:write`: site.save_content, site.publish, site.discard, site.rollback - `strategy:read`: strategy.list_profiles, strategy.get_profile, strategy.network, strategy.search_library, strategy.build_file, strategy.waves, strategy.wave_file, strategy.run_status, strategy.wave_runs, strategy.wave_group_file - `strategy:write`: strategy.create_profile, strategy.update_profile, strategy.add_competitor, strategy.add_competitors, strategy.add_note, strategy.research, strategy.analyze, strategy.save_update, strategy.set_revisions, strategy.build_settings, strategy.start_wave, strategy.stop_wave, strategy.ship_wave, strategy.ship_wave_group, strategy.suggest, strategy.coach, strategy.plan - `stream:read`: stream.list_events, stream.get_event, stream.list_destinations, stream.list_chat, stream.get_summary, stream.list_questions, stream.show_report - `stream:write`: stream.create_event, stream.update_event, stream.end_event, stream.save_destination, stream.delete_destination, stream.feature_comment, stream.clear_comment, stream.post_chat, stream.set_rundown, stream.captions_token, stream.export, stream.create_poll, stream.vote, stream.ask - `studios:read`: studios.usage, studios.balance, studios.health - `talk:read`: talk.list_conversations, talk.list_messages, talk.export_transcript, talk.get_settings, talk.list_members, talk.list_pins, talk.browse_channels - `talk:write`: talk.send_message, talk.create_workspace, talk.add_member, talk.create_channel, talk.open_dm, talk.update_settings, talk.add_people, talk.remove_person, talk.set_role, talk.pin_message, talk.join_channel - `update:read`: update.topics, update.list, update.status, update.subscribers - `update:write`: update.publish, update.subscribe, update.unsubscribe `*` holds all of them. ## Money - Prices are in credits: 1,000 credits = $1. - **API balance** pays the per-call prices. It is spendable across every product; there is no per-product wallet to keep topped up. - **Plan credits** come with a BOLD Studios plan and pay for the heavy work an operation starts: Strategy research and analysis, live and recorded minutes, 24/7 channels, QA runs. The product charges them when the work succeeds and the gateway adds nothing on top. - **Charged after success only.** A failed call costs nothing, whether it failed at 401 or at 502. There is no compensating logic to write. - Every account gets 2,000 free credits ($2.00) of API balance with its first key. - `GET https://boldstudios.io/api/v1/balance` is free and shows both balances. Check it before a batch. - Auto-reload can top the balance up when it falls below a floor you set, under a hard daily ceiling so a runaway loop cannot drain a card. - Every key carries its own daily spend cap, 500 cents on a newly minted one. It is the control worth tightening on anything you hand to an agent. Prices, live from the catalog: | Operation | Paid from | Price | | --- | --- | --- | | `campaign.list` | Free | free | | `talk.list_conversations` | Free | free | | `mail.list_mailboxes` | Free | free | | `embed.create` | API balance | 10 credits ($0.01) per embed | | `studios.usage` | Free | free | | `send.list_brands` | Free | free | | `ops.list_builds` | Free | free | | `crm.list_workspaces` | Free | free | | `strategy.list_profiles` | Free | free | | `clip.list_projects` | Free | free | | `site.list_projects` | Free | free | | `stream.create_event` | Included | Free to create. While live: 40 credits ($0.04) per minute per live output, 4 credits ($0.004) per person per minute, 40 credits ($0.04) per cloud recording minute; 4K recording adds 50 credits ($0.05) per minute. 720p and 1080p recording are included | | `refer.upsert_program` | Free | free | | `promote.plan` | Free | free | | `qr.create` | API balance | 20 credits ($0.02) per code | | `network.list` | Free | free | | `qa.run` | Plan credits | 500 credits ($0.50) per run, charged only when the run reaches a verdict | | `artist.list_releases` | Free | free | | `sign.create_document` | API balance | 100 credits ($0.10) per envelope | | `royalties.balances` | Free | free | | `calendar.list` | Free | free | | `update.topics` | Free | free | | `ai.chat` | API balance | 10 credits ($0.01) per 1k input tokens; per 1k output tokens 10 credits ($0.01) on flash, 20 credits ($0.02) on pro, 50 credits ($0.05) on max | | `publish.list_posts` | Free | free | | `stream.list_events` | Free | free | | `embed.list` | Free | free | | `talk.list_messages` | Free | free | | `qa.get` | Free | free | | `artist.get_release` | Free | free | | `royalties.list_releases` | Free | free | | `mail.create_mailbox` | Plan allowance | A seat on your BOLD Mail plan; up to 10 mailboxes without a subscription | | `send.list_domains` | Free | free | | `crm.search_contacts` | Free | free | | `ops.get_build` | Free | free | | `update.publish` | API balance | 10 credits ($0.01) per delivery | | `campaign.get` | Free | free | | `clip.list_sources` | Free | free | | `calendar.create` | API balance | 20 credits ($0.02) per calendar | | `refer.upsert_partner` | Free | free | | `network.guide` | Free | free | | `sign.list_documents` | Free | free | | `strategy.create_profile` | Free | free | | `ai.generate_image` | API balance | 250 credits ($0.25) per image | | `qr.list` | Free | free | | `site.get_content` | Free | free | | `promote.create_campaign` | Free | free | | `studios.balance` | Free | free | | `strategy.update_profile` | Free | free | | `talk.send_message` | Free | free | | `artist.list_links` | Free | free | | `qa.list` | Free | free | | `send.list_contacts` | Free | free | | `royalties.import_statement` | Free | free | | `strategy.get_profile` | Free | free | | `sign.get_document` | Free | free | | `mail.list_messages` | Free | free | | `network.now` | Free | free | | `embed.delete` | Free | free | | `qr.stats` | API balance | 10 credits ($0.01) per call | | `ai.transcribe` | API balance | 50 credits ($0.05) per minute | | `ops.list_tasks` | Free | free | | `crm.get_contact` | Free | free | | `stream.get_event` | Free | free | | `calendar.list_events` | Free | free | | `refer.conversion` | API balance | 10 credits ($0.01) per call | | `update.list` | Free | free | | `site.save_content` | Free | free | | `clip.list_clips` | Free | free | | `strategy.network` | Free | free | | `ai.transcript` | API balance | 50 credits ($0.05) per minute | | `strategy.search_library` | Free | free | | `clip.get_clip` | Free | free | | `ops.list_runs` | Free | free | | `artist.royalty_coverage` | Free | free | | `royalties.set_shares` | Free | free | | `crm.upsert_contact` | Free | free | | `publish.analytics` | API balance | 10 credits ($0.01) per call | | `publish.list_destinations` | Free | free | | `sign.audit_trail` | Free | free | | `calendar.create_event` | API balance | 10 credits ($0.01) per entry | | `update.status` | Free | free | | `send.upsert_contact` | Free | free | | `site.publish` | Free | free | | `mail.get_message` | Free | free | | `talk.export_transcript` | Free | free | | `embed.stats` | Free | free | | `refer.partner_summary` | Free | free | | `strategy.add_competitor` | Free | free | | `stream.update_event` | Free | free | | `network.create_slot` | Included | Free to schedule. A 24/7 channel bills 40 credits ($0.04) per minute on air | | `publish.create_destination` | Free | free | | `publish.rotate_destination` | Free | free | | `publish.delete_destination` | Free | free | | `strategy.add_competitors` | Free | free | | `strategy.add_note` | Free | free | | `mail.send` | Plan allowance | Draws on the mailbox's hourly and daily sending limits in your BOLD Mail plan | | `crm.add_note` | Free | free | | `update.subscribers` | Free | free | | `stream.end_event` | Free | free | | `calendar.slots` | Free | free | | `site.discard` | Free | free | | `send.delete_contact` | Free | free | | `strategy.research` | Plan credits | 500 credits ($0.50) per new competitor, 300 credits ($0.30) per refresh, free when the shared library already has it fresh | | `network.cancel_slot` | Free | free | | `sign.void_document` | Free | free | | `refer.payouts_due` | Free | free | | `talk.create_workspace` | Free | free | | `promote.console` | Free | free | | `royalties.record_payment` | Free | free | | `ops.ledger` | Free | free | | `promote.report` | Free | free | | `mail.mark` | Free | free | | `network.asrun` | Free | free | | `update.subscribe` | Free | free | | `calendar.book` | API balance | 50 credits ($0.05) per booking | | `crm.list_segments` | Free | free | | `send.list_lists` | Free | free | | `stream.list_destinations` | Free | free | | `strategy.analyze` | Plan credits | 250 credits ($0.25) to compare, 60 credits ($0.06) per re-scored competitor, 400 credits ($0.40) for the path; free when nothing changed | | `royalties.my_statement` | Free | free | | `talk.add_member` | Free | free | | `ops.list_playbooks` | Free | free | | `site.versions` | Free | free | | `refer.stats` | Free | free | | `strategy.save_update` | Free | free | | `strategy.build_file` | Free | free | | `strategy.set_revisions` | Free | free | | `strategy.build_settings` | Free | free | | `strategy.waves` | Free | free | | `strategy.wave_file` | Free | free | | `stream.save_destination` | Free | free | | `strategy.start_wave` | Plan credits | Each pack is built by BOLD Ops and billed by tokens used: 25 credits ($0.025) per 1k tokens on Sonnet, 100 credits ($0.10) on Opus | | `network.log_asrun` | Free | free | | `update.unsubscribe` | Free | free | | `calendar.list_bookings` | Free | free | | `crm.segment_members` | Free | free | | `talk.create_channel` | Free | free | | `ops.create_brief` | Free | free | | `send.create_list` | API balance | 20 credits ($0.02) per list | | `strategy.run_status` | Free | free | | `site.rollback` | Free | free | | `strategy.wave_runs` | Free | free | | `strategy.stop_wave` | Free | free | | `strategy.ship_wave` | Free | free | | `strategy.wave_group_file` | Free | free | | `strategy.ship_wave_group` | Free | free | | `stream.delete_destination` | Free | free | | `talk.open_dm` | Free | free | | `calendar.cancel_booking` | Free | free | | `site.status` | Free | free | | `ops.save_blueprint` | Free | free | | `network.audience` | Free | free | | `send.update_list_members` | Free | free | | `strategy.suggest` | Plan credits | 150 credits ($0.15) per call | | `crm.followups` | Free | free | | `network.list_bookings` | Free | free | | `strategy.coach` | Plan credits | 100 credits ($0.10) per reply | | `ops.import_tasks` | Free | free | | `send.list_suppressions` | Free | free | | `talk.get_settings` | Free | free | | `stream.feature_comment` | Free | free | | `stream.clear_comment` | Free | free | | `stream.post_chat` | Free | free | | `stream.list_chat` | Free | free | | `stream.set_rundown` | Free | free | | `stream.captions_token` | Free | free | | `stream.get_summary` | Free | free | | `stream.export` | Free | free | | `strategy.plan` | Plan credits | 400 credits ($0.40) per plan | | `network.book_guest` | Free | free | | `send.suppress` | Free | free | | `ops.approve_blueprint` | Plan credits | The agent's work is billed in plan credits by tokens used: 10 credits ($0.01) per 1k tokens on Haiku, 25 credits ($0.025) on Sonnet, 100 credits ($0.10) on Opus | | `talk.update_settings` | Free | free | | `send.list_campaigns` | Free | free | | `ops.fork_playbook` | Free | free | | `talk.list_members` | Free | free | | `stream.create_poll` | Free | free | | `network.update_booking` | Free | free | | `send.create_campaign` | API balance | 50 credits ($0.05) per campaign | | `studios.health` | Free | free | | `ops.start_session` | Plan credits | The agent's work is billed in plan credits by tokens used: 10 credits ($0.01) per 1k tokens on Haiku, 25 credits ($0.025) on Sonnet, 100 credits ($0.10) on Opus | | `network.rundown` | Free | free | | `talk.add_people` | Free | free | | `stream.vote` | Free | free | | `monitor.status` | Free | free | | `send.get_email` | Free | free | | `talk.remove_person` | Free | free | | `stream.list_questions` | Free | free | | `ops.get_session` | Free | free | | `talk.set_role` | Free | free | | `send.domain_health` | Free | free | | `stream.ask` | Free | free | | `ops.list_sessions` | Free | free | | `stream.show_report` | Free | free | | `send.sync_domains` | Free | free | | `talk.list_pins` | Free | free | | `ops.steer` | Plan credits | The agent's work is billed in plan credits by tokens used: 10 credits ($0.01) per 1k tokens on Haiku, 25 credits ($0.025) on Sonnet, 100 credits ($0.10) on Opus | | `ops.stop` | Free | free | | `talk.pin_message` | Free | free | | `talk.browse_channels` | Free | free | | `ops.answer` | Plan credits | The agent's work is billed in plan credits by tokens used: 10 credits ($0.01) per 1k tokens on Haiku, 25 credits ($0.025) on Sonnet, 100 credits ($0.10) on Opus | | `ops.approve` | Plan credits | The agent's work is billed in plan credits by tokens used: 10 credits ($0.01) per 1k tokens on Haiku, 25 credits ($0.025) on Sonnet, 100 credits ($0.10) on Opus | | `talk.join_channel` | Free | free | ## Rate limits Per key, and the same three numbers on every key issued so far: | Window | Limit | | --- | --- | | Per minute | 60 requests | | Per hour | 2000 requests | | Per day | 20000 requests | - Fixed calendar windows in UTC, not sliding. The minute window resets on the minute, the hour on the hour, and the day at 00:00 UTC. A burst at 23:59:59 and another at 00:00:01 both succeed. - Every call that gets as far as the rate check is counted, including free operations and including calls that then fail on money or on validation. Calls refused earlier, for a bad key or a missing scope, are not. - 429 rate_limited, carrying `limit`, `window` (minute, hour or day) and a `Retry-After` in seconds, which is the time left in that window rather than a fixed backoff. - Separately from the windows, repeated abuse can block a key, its account or an IP. That is also a 429, with name `blocked` and an `until`. Retrying during a block extends it. - These are per-key columns. A higher ceiling is a support conversation, not a plan tier. ## Pagination - `limit`, default **25**, maximum **100**. Over the maximum is clamped down, not refused. A limit that is absent, unparseable, zero or negative falls back to the default. You will never get a 422 for a bad limit, so check what you got rather than what you asked for. - Newest first on every list. - **There is no cursor, no offset, no page number, no total and no has_more. A list is the newest N and nothing else, so an account with more than 100 of something cannot page past the first 100 today.** - The reads that take a period take `days` instead: default 30, maximum 365, clamped the same way. publish.analytics also accepts explicit `from` and `to` dates, which override `days`. - Two operations read a bounded slice underneath: studios.usage reads at most the 5,000 most recent calls in the window, and publish.analytics at most 10,000 daily rows. Takes `limit`: `qr.list`, `publish.list_posts`, `sign.list_documents`. ## Idempotency, exactly Send `Idempotency-Key: ` on a paid call, up to 256 characters. **It deduplicates THE CHARGE, not the work. The operation runs every time. Send the same key twice to ai.chat and you get a second completion, generated and returned; you are simply not billed for it again.** It is not a cached response and it is not a way to make a create idempotent. Two qr.create calls with one key make two tracked links and bill once. Use it to make a RETRY safe, not to make a duplicate harmless. - **Scope.** Scoped to your account. Two accounts can send the same key on the same day without colliding, and only your own earlier call can make a later one a replay. Use a UUID anyway: within one account a reused string is a charge that silently does not happen, and a uuid is the only value you can be sure you have not used before. - **How long.** Remembered for as long as the ledger row exists, which is indefinitely. There is no expiry window after which the same key becomes chargeable again. - **Same key, different body.** Nothing compares the body. The same key with a different body runs the new request and returns the new result, charging nothing. That is the one way to spend a key on work you did not mean to pay for, so regenerate the key whenever the request changes. - **Free operations.** A free operation never reaches the charge path, so `X-BOLD-Idempotent-Replay` never appears on one. - **Transcription.** ai.transcribe and ai.transcript override the key with the transcript id, so one audio file is billed once however many times it is polled and whatever you send in the header. - **No header.** With no Idempotency-Key, `X-BOLD-Request-Id` is used, and with neither a fresh uuid is generated, which means no deduplication at all. - **Telling.** A deduplicated charge answers `X-BOLD-Idempotent-Replay: true` and `X-BOLD-Charged-Cents: 0`. ## Versioning and deprecation - One version, in the path. There is no version header and no dated version. Current: `v1`. - `GET /v1` is the contract. Operations are added and retired there. - Additive changes ship without notice: a new field on a response, a new optional request field, a new operation, a new error `name`. Ignore response fields you do not know rather than failing on them. - A breaking change gets a new path. `/v1` is not changed underneath you. - An operation being retired is deactivated first: it stops appearing in `GET /v1` and answers 503 operation_disabled, which is retryable, so a client that polls the catalog notices before a client that does not. Withdrawn entirely, it answers 404 unknown_operation. - BOLD does not send `Deprecation` or `Sunset` headers today, and there is no changelog feed. The mechanism is polling: fetch `GET /v1` or `/v1/openapi.json` on a schedule and diff it. That is the honest answer rather than a promise of headers that are not sent. ## Test keys and live keys **THERE IS NO SANDBOX. A key labelled test is not a test key: it authenticates identically, calls the same providers, and spends the same real cents from the same wallet.** - `boldapi_keys.environment` defaults to `live` and is returned by the authorizer, but nothing in the gateway reads it. Authentication matches on the prefix and the secret only, so even the `` segment of the string you present is not checked against the stored value. - Use the free operations, which are genuinely free: studios.balance, studios.usage, qr.list, sign.list_documents, sign.get_document, sign.audit_trail, publish.list_posts and every publish destination call. They exercise auth, scopes, rate limits and response shapes without spending anything. - A newly minted key carries a `daily_cents_cap` of 500 cents, so a runaway loop costs five dollars rather than a wallet. Lower it for anything you hand to an agent, and raise it deliberately when a real workload needs to. ## Limits and conventions - **Daily spend cap.** Every newly minted key carries a `daily_cents_cap` of 500 cents: a per-key ceiling on spend per UTC day, not a balance. Over it the call is refused 402 `daily_cap_reached`, carrying `spent_cents` and `cap_cents`, and nothing is charged. Raise it, lower it or clear it per key from the developers page. A key with no cap has no ceiling beyond the account balance, which is how every key minted before this default was introduced behaves. - **Body size.** The gateway sets no body size limit of its own; an oversized request is refused by the platform in front of it, before any operation is resolved. Individual fields are capped and the cap is in each field's schema: 2,048 characters for a URL, 1,200 for an image prompt, 300 for a document title, 120 for a destination name, and 2,000 for a string with no cap of its own. - **Timeouts.** Most calls answer in well under a second. ai.transcribe deliberately holds the connection open for up to 90 seconds waiting for a short file, so allow at least 120 seconds of client timeout on that one and on nothing else. A function that runs past the platform's own ceiling is terminated and answers 5xx, and nothing is charged. - **Streaming.** No operation streams. Every response is a single JSON body. - **Content type.** Send `Content-Type: application/json` on any call with a body. A body that is not valid JSON is a 422. - **Methods.** GET and DELETE never carry a body; anything sent with one is ignored. - **CORS.** Every endpoint answers CORS preflight and allows any origin, but a browser call means shipping a key to the browser. Do not. - **Placeholders.** Placeholders in this documentation are written as ``, for example `bold_live__`. Ids in worked examples are well formed but fictional: they are the right shape to paste into a client and the wrong value to send to the API, which will answer 404 rather than touching anything real. ### Webhooks BOLD Publish can post to an endpoint of yours, signed. The whole receiver contract, including the signature algorithm, the timestamp tolerance, the delivery statuses and the retry schedule, is at `https://boldstudios.io/api/v1/docs/webhooks.md`. ## Response headers | Header | Meaning | | --- | --- | | `X-BOLD-Request-Id` | Echoes what you sent, or a new id. Log it. | | `X-BOLD-Operation` | Which catalog operation ran. | | `X-BOLD-Price-Cents` | List price of one unit. | | `X-BOLD-Charged-Cents` | What this call cost. Zero on failure and on a replay. | | `X-BOLD-Idempotent-Replay` | `true` when a key matched an earlier call. | | `X-BOLD-Balance-Cents` | What is left. | | `Retry-After` | Seconds to wait, on a 429. | ## Errors Every failure is `{ statusCode, name, message }`, plus extra fields on some. **Switch on `name`.** Retry with backoff: `rate_limited`, `blocked`, `application_error`, `upstream_error`, `operation_disabled`, `service_unavailable`. Nothing else changes its mind. | Status | name | When | What to do | | --- | --- | --- | --- | | 401 | `missing_api_key` | No Authorization header, or one that is not a Bearer token. | Send `Authorization: Bearer bold_live__`. | | 401 | `invalid_key` | The key does not match any key on file. Usually a truncated paste or the wrong environment. | Check the whole key was copied. Mint another at the developers page if it is lost. | | 401 | `key_revoked` | The key was revoked by its owner. | Mint a new key. Retrying will never work. | | 401 | `key_expired` | The key had an expiry and it has passed. | Mint a new key, optionally without an expiry. | | 402 | `insufficient_credits` | The balance does not cover this call. The key is fine. | Top up, or turn on auto-reload. Nothing was charged. | | 402 | `daily_cap_reached` | This key has its own daily spend ceiling and today's spend reached it. | Raise or clear the cap on that key, or wait for the next UTC day. The balance is untouched. | | 403 | `scope_denied` | The key is valid but does not hold the scope this operation needs. | Mint a key with the scope named in `required_scope`, or with `*`. | | 404 | `not_found` | The path matches no published operation, or the resource id is not yours. | GET /v1 lists every operation. A resource you do not own reads as missing on purpose. | | 404 | `unknown_operation` | The operation resolved but is not in the catalog any more. | Re-read GET /v1. Operations are added and retired there, not in a changelog. | | 405 | `method_not_allowed` | The path exists but not with that verb. | GET /v1 names the method for each operation. | | 422 | `validation_error` | The body is not valid JSON, or a field is the wrong shape. | Fix the request. Nothing was charged. | | 422 | `missing_required_field` | A field the operation cannot run without was absent. | The message names the field. Nothing was charged. | | 422 | `transcription_failed` | The audio was reached but could not be transcribed. | Check the file is real audio and the URL is readable without a credential. Nothing was charged. | | 429 | `rate_limited` | One of the three per-key windows tripped: minute, hour or day. | Wait `Retry-After` seconds. The body names which window and its limit. | | 429 | `blocked` | The key is temporarily blocked after repeated abuse or a burst of failures. | The body carries `until`. Do not hammer it: retries extend the block. | | 500 | `application_error` | Something broke on the BOLD side. | Nothing was charged. Retry once, then quote `X-BOLD-Request-Id` if it persists. | | 501 | `not_implemented` | The operation is published in the catalog but is not wired up yet. | Nothing was charged. GET /v1 shows what is live today. | | 502 | `upstream_error` | A provider behind the operation failed or timed out. | Nothing was charged. Retry with backoff. | | 503 | `operation_disabled` | The operation was turned off in the catalog, usually during maintenance. | Retry later. GET /v1 stops listing it while it is off. | | 503 | `service_unavailable` | A dependency the operation needs is not configured or not answering. | Nothing was charged. Retry with backoff. | - `insufficient_credits` also carries `balance_cents`, `price_cents`, `add_credit_url`. - `daily_cap_reached` also carries `spent_cents`, `cap_cents`, `add_credit_url`. - `scope_denied` also carries `required_scope`. - `rate_limited` also carries `limit`, `window`. - `blocked` also carries `until`, `severity`. ## Quickstart ### curl ```bash export BOLD_API_KEY=bold_live_... # Free. Proves the key works and shows what is left. curl -s https://boldstudios.io/api/v1/balance -H "Authorization: Bearer $BOLD_API_KEY" # A chat completion (10 credits ($0.01) per 1k input tokens; per 1k output tokens 10 credits ($0.01) on flash, 20 credits ($0.02) on pro, 50 credits ($0.05) on max) curl -s -X POST 'https://boldstudios.io/api/v1/ai/chat' \ -H "Authorization: Bearer $BOLD_API_KEY" \ -H 'Content-Type: application/json' \ -H "Idempotency-Key: $(uuidgen)" \ -d '{"model":"pro","max_tokens":512,"messages":[{"role":"user","content":"Write one line of launch copy for a coffee subscription."}]}' ``` ### TypeScript ```ts // Zero dependencies, so fetch is enough. The SDK is a convenience, not a requirement. const BASE = "https://boldstudios.io/api/v1"; const key = process.env.BOLD_API_KEY!; async function bold(path: string, init: RequestInit = {}) { const res = await fetch(BASE + path, { ...init, headers: { Authorization: `Bearer ${key}`, "Content-Type": "application/json", // Any string you can regenerate. A retry with the same one is free. "Idempotency-Key": crypto.randomUUID(), ...(init.headers || {}), }, }); const body = await res.json(); if (!res.ok) { // Switch on body.name, never on body.message. throw Object.assign(new Error(body.message), { name: body.name, status: res.status, body }); } return body; } console.log(await bold("/balance")); // free await bold("/ai/chat", { method: "POST", body: JSON.stringify({"model":"pro","max_tokens":512,"messages":[{"role":"user","content":"Write one line of launch copy for a coffee subscription."}]}) }); ``` ### Python ```python import os, uuid, requests BASE = "https://boldstudios.io/api/v1" KEY = os.environ["BOLD_API_KEY"] def bold(path, method="GET", json=None): r = requests.request(method, BASE + path, json=json, headers={ "Authorization": f"Bearer {KEY}", "Idempotency-Key": str(uuid.uuid4()), # a retry with the same key is free }) body = r.json() if not r.ok: # Switch on body['name'], never on body['message']. raise RuntimeError(f"{body['name']}: {body['message']}") return body print(bold("/balance")) # free bold("/ai/chat", method="POST", json={"model":"pro","max_tokens":512,"messages":[{"role":"user","content":"Write one line of launch copy for a coffee subscription."}]}) ``` ## MCP `https://boldstudios.io/api/v1/mcp`, Streamable HTTP, protocol 2025-11-25 (2025-06-18 and 2025-03-26 also accepted). Operations are tools and every doc page is a resource, so an agent can read the reference and make the call over one connection. Image generation, chat completions, ad creative and campaign launch are REST only. Two ways to authenticate: - **Sign in (OAuth 2.1).** Add the URL as a connector in Claude, ChatGPT or any client that supports MCP authorization. An unauthenticated request gets `401` with `WWW-Authenticate: Bearer resource_metadata="https://boldstudios.io/.well-known/oauth-protected-resource/api/v1/mcp"`, and the client takes it from there: PKCE S256, public clients, Client ID Metadata Documents or dynamic registration. Approving creates an API key named after the client on your account, so it shows on the developers page with its usage, and revoking it there disconnects the client. - **API key.** Send `Authorization: Bearer bold_live__`, exactly as for REST: ```bash claude mcp add --transport http bold https://boldstudios.io/api/v1/mcp --header "Authorization: Bearer $BOLD_API_KEY" ``` ```json { "mcpServers": { "bold": { "type": "http", "url": "https://boldstudios.io/api/v1/mcp", "headers": { "Authorization": "Bearer bold_live__" } } } } ``` Tool names are the operation name with dots replaced by underscores, so `ai.chat` is `ai_chat`. `tools/call` runs the identical authorize, rate limit and charge path as REST: MCP is not a cheaper way in. ## Privacy - Per call BOLD stores the operation, units, price, latency, a truncated IP (IPv4 to /24, IPv6 to /48) and the user agent. The whole address is never written down. - The IP and user agent are erased after thirty days. The billing figures are kept, because they are the bill. - Emails, phone numbers, IP addresses, user agents, account ids, tokens and secrets are stripped from every response before it leaves. - Nothing sent through this API is used to train anything. ## Operations ### BOLD Publish Schedule and publish content to social networks and to your own platform. #### publish.list_posts `GET https://boldstudios.io/api/v1/publish/posts` Scope `publish:read`. free. List scheduled and published posts Scheduled and published posts for the BOLD Publish brands connected to this account, newest first, each with its network, publish time, text, link and the impressions and interactions recorded so far. Filter with brand_id, network, since, until and limit, which defaults to 25 and caps at 100. Free. An account with no brands connected gets an empty list and a note rather than an error, so read the brands array in the response before concluding there is nothing scheduled. ```bash curl 'https://boldstudios.io/api/v1/publish/posts?limit=25&network=instagram' -H "Authorization: Bearer $BOLD_API_KEY" ``` Returns: ```json { "posts": [ { "network": "instagram", "published_at": "2026-08-30T14:02:00Z", "text": "New drop lands Friday. Link in bio.", "impressions": 4210, "interactions": 318 } ], "brands": [ "4c7e0f9a-6b21-4d83-9f5c-2a1e7d0b8c34" ] } ``` #### publish.analytics `GET https://boldstudios.io/api/v1/publish/analytics` Scope `publish:read`. 10 credits ($0.01) per call. Engagement for a brand over a date range Daily engagement for your BOLD Publish brands over a date range, returned both as the raw daily rows and as totals rolled up by network and metric. The roll up honours each metric's own aggregation, so impressions are summed across the window while a follower count is taken as the peak rather than added up, which is why the totals are not simply the sum of the rows. Costs 1c per call, unlike the free publish.list_posts, and it covers days back from today (default 30, maximum 365) unless you pass explicit from and to dates. ```bash curl 'https://boldstudios.io/api/v1/publish/analytics?days=30' -H "Authorization: Bearer $BOLD_API_KEY" ``` Returns: ```json { "from": "2026-08-03", "to": "2026-09-02", "totals": [ { "network": "instagram", "slot": "impressions", "agg": "sum", "value": 128400, "days": 30 } ] } ``` #### publish.list_destinations `GET https://boldstudios.io/api/v1/publish/destinations` Scope `publish:read`. free. List your destinations Every platform of yours that receives scheduled posts, without the signing keys. ```bash curl 'https://boldstudios.io/api/v1/publish/destinations?brand_id=4c7e0f9a-6b21-4d83-9f5c-2a1e7d0b8c34' -H "Authorization: Bearer $BOLD_API_KEY" ``` Returns: ```json { "ok": true, "destinations": [ { "id": "5a91b73c-08de-4f27-b1a6-9c4d5e2f0731", "brand_id": "4c7e0f9a-6b21-4d83-9f5c-2a1e7d0b8c34", "name": "Acme ops webhook", "endpoint_url": "https://acme.example.com/hooks/bold", "is_active": true, "created_at": "2026-08-28T11:20:00Z", "last_delivery_at": "2026-09-01T16:45:00Z" } ] } ``` #### publish.create_destination `POST https://boldstudios.io/api/v1/publish/destinations` Scope `publish:write`. free. Register a destination Registers an https endpoint that will receive your scheduled posts. Returns the signing key once. ```bash curl -X POST 'https://boldstudios.io/api/v1/publish/destinations' \ -H "Authorization: Bearer $BOLD_API_KEY" \ -H 'Content-Type: application/json' \ -d '{"brand_id":"4c7e0f9a-6b21-4d83-9f5c-2a1e7d0b8c34","name":"Acme ops webhook","endpoint_url":"https://acme.example.com/hooks/bold"}' ``` Returns: ```json { "ok": true, "id": "5a91b73c-08de-4f27-b1a6-9c4d5e2f0731", "brand_id": "4c7e0f9a-6b21-4d83-9f5c-2a1e7d0b8c34", "name": "Acme ops webhook", "endpoint_url": "https://acme.example.com/hooks/bold", "signing_key": "bdsk_3f9c1a7e52d80b46af13e6c94d27b085f0a31c6e8b25d4790fa6c3e18b5d02a7", "note": "The signing key is shown once. Store it now and verify it on every delivery." } ``` #### publish.rotate_destination `POST https://boldstudios.io/api/v1/publish/destinations/{id}/rotate` Scope `publish:write`. free. Rotate a signing key Issues a new signing key and invalidates the old one immediately. ```bash curl 'https://boldstudios.io/api/v1/publish/destinations/{id}/rotate' -H "Authorization: Bearer $BOLD_API_KEY" ``` Returns: ```json { "ok": true, "id": "5a91b73c-08de-4f27-b1a6-9c4d5e2f0731", "signing_key": "bdsk_3f9c1a7e52d80b46af13e6c94d27b085f0a31c6e8b25d4790fa6c3e18b5d02a7", "rotated_at": "2026-09-02T10:05:00Z", "note": "The previous key stopped working the moment this one was issued." } ``` #### publish.delete_destination `DELETE https://boldstudios.io/api/v1/publish/destinations/{id}` Scope `publish:write`. free. Remove a destination Deactivates the destination and cancels anything still queued for it. ```bash curl 'https://boldstudios.io/api/v1/publish/destinations/{id}' -H "Authorization: Bearer $BOLD_API_KEY" ``` Returns: ```json { "ok": true, "id": "5a91b73c-08de-4f27-b1a6-9c4d5e2f0731" } ``` ### BOLD QR Tracked short links and QR codes that keep resolving, including after a subscription lapses. #### qr.create `POST https://boldstudios.io/api/v1/qr/codes` Scope `qr:write`. 20 credits ($0.02) per code. Create a tracked short link Returns a boldstudios.io/q/ short link and the id you use to read its scans. The link keeps resolving for as long as the code exists, including after a plan lapses, which is the one thing every competitor takes away. ```bash curl -X POST 'https://boldstudios.io/api/v1/qr/codes' \ -H "Authorization: Bearer $BOLD_API_KEY" \ -H 'Content-Type: application/json' \ -d '{"title":"Spring flyer","target_url":"https://example.com/spring","ec_level":"M"}' ``` Returns: ```json { "id": "9b1c7f42-3ad8-4e05-9c61-0f2b8d6a4e37", "slug": "k3Rm9Ty", "short_url": "https://boldstudios.io/q/k3Rm9Ty", "title": "Spring flyer", "target_url": "https://example.com/spring", "ec_level": "M" } ``` #### qr.list `GET https://boldstudios.io/api/v1/qr/codes` Scope `qr:read`. free. List your tracked links Your own codes, newest first, with lifetime scan counts. Free. ```bash curl 'https://boldstudios.io/api/v1/qr/codes?limit=25' -H "Authorization: Bearer $BOLD_API_KEY" ``` Returns: ```json { "codes": [ { "id": "9b1c7f42-3ad8-4e05-9c61-0f2b8d6a4e37", "slug": "k3Rm9Ty", "short_url": "https://boldstudios.io/q/k3Rm9Ty", "title": "Spring flyer", "total_scans": 412, "unique_scans": 380 } ] } ``` #### qr.stats `GET https://boldstudios.io/api/v1/qr/codes/{id}/scans` Scope `qr:read`. 10 credits ($0.01) per call. Scans for one link Daily scan and unique counts plus a country breakdown. Aggregates only: no IP, device or browser data about the people who scanned leaves this API. ```bash curl 'https://boldstudios.io/api/v1/qr/codes/{id}/scans?days=30' -H "Authorization: Bearer $BOLD_API_KEY" ``` Returns: ```json { "id": "9b1c7f42-3ad8-4e05-9c61-0f2b8d6a4e37", "slug": "k3Rm9Ty", "total_scans": 412, "unique_scans": 380, "period_days": 30, "daily": [ { "date": "2026-09-01", "scans": 22, "unique": 20 } ], "by_country": [ { "country": "US", "scans": 340 } ] } ``` ### BOLD Embed Put a live card of your own data on somebody else's website, and turn the same data into a 1200x630 share image. Themed, auto-sizing, measured, and always current. #### embed.create `POST https://boldstudios.io/api/v1/embed/embeds` Scope `embed:write`. 10 credits ($0.01) per embed. Create or update an embed Creates an embeddable card and returns its token, the widget URL, the share-image URL and a paste-ready snippet. Idempotent on (kind, ref): calling it again for the same subject updates that embed rather than minting a second token, which matters because the first token is already pasted into somebody's website. ```bash curl -X POST 'https://boldstudios.io/api/v1/embed/embeds' \ -H "Authorization: Bearer $BOLD_API_KEY" \ -H 'Content-Type: application/json' \ -d '{"kind":"scorecard","ref":"jane-doe","mode":"fetch","source_path":"data/scorecard/jane-doe","width":480,"allowed_domains":["janedoe.example.com"]}' ``` Returns: ```json { "ok": true, "token": "e_0f3a9c7d21b4482e9a5c6d8e1f204b73", "kind": "scorecard", "ref": "jane-doe", "embed_url": "https://boldstudios.io/embed/v/e_0f3a9c7d21b4482e9a5c6d8e1f204b73", "image_url": "https://boldstudios.io/embed/i/e_0f3a9c7d21b4482e9a5c6d8e1f204b73.png", "snippet": "
" } ``` #### embed.list `GET https://boldstudios.io/api/v1/embed/embeds` Scope `embed:read`. free. List your embeds Every embed on your property, newest first. Returns the token, kind, ref, mode, width, enabled state and domain allowlist. Does not return the blocks; read one with embed.create's idempotent behaviour or render it. ```bash curl 'https://boldstudios.io/api/v1/embed/embeds?kind=scorecard&limit=50' -H "Authorization: Bearer $BOLD_API_KEY" ``` Returns: ```json { "ok": true, "property": "acme", "embeds": [ { "token": "e_0f3a9c7d21b4482e9a5c6d8e1f204b73", "kind": "scorecard", "ref": "jane-doe", "title": "Jane Doe", "mode": "fetch", "width": 480, "enabled": true, "allowed_domains": [ "janedoe.example.com" ], "created_at": "2026-09-03T12:18:34Z", "updated_at": "2026-09-03T12:18:34Z" } ] } ``` #### embed.delete `DELETE https://boldstudios.io/api/v1/embed/embeds/{token}` Scope `embed:write`. free. Delete an embed Removes the embed and its counters. Anything already pasted into a website stops rendering, so this is the call to make when a card should no longer exist rather than the one to make to hide it: set enabled false through embed.create for that. ```bash curl 'https://boldstudios.io/api/v1/embed/embeds/{token}' -H "Authorization: Bearer $BOLD_API_KEY" ``` Returns: ```json { "ok": true, "deleted": true, "token": "e_0f3a9c7d21b4482e9a5c6d8e1f204b73" } ``` #### embed.stats `GET https://boldstudios.io/api/v1/embed/stats` Scope `embed:read`. free. Views, clicks and where from Views, clicks and share-image renders, by day and by the domain that showed the card. Pass a token for one embed or leave it off for the whole property. There is no visitor identity in here at all: the service stores a day, a domain and a count, and no IP, user agent or identifier of any kind. ```bash curl 'https://boldstudios.io/api/v1/embed/stats?days=30&token=e_0f3a9c7d21b4482e9a5c6d8e1f204b73' -H "Authorization: Bearer $BOLD_API_KEY" ``` Returns: ```json { "ok": true, "property": "acme", "stats": { "from": "2026-08-05", "to": "2026-09-03", "totals": { "views": 1840, "clicks": 96, "images": 212, "embeds": 34 }, "by_day": [ { "day": "2026-09-03", "views": 71, "clicks": 4 } ], "top_domains": [ { "domain": "janedoe.example.com", "views": 1290 } ] } } ``` ### BOLD Refer Referral links, first-touch attribution, commission with a hold period, and payouts. The parts that are not the link. #### refer.upsert_program `POST https://boldstudios.io/api/v1/refer/programs` Scope `refer:write`. free. Create or update a programme A programme is the commercial shape: percent or flat, the rate, whether it pays on the first sale or every one, the attribution window, the hold period before commission is payable, and the payout threshold. All of it is data, so repricing is this call rather than a deploy. ```bash curl -X POST 'https://boldstudios.io/api/v1/refer/programs' \ -H "Authorization: Bearer $BOLD_API_KEY" \ -H 'Content-Type: application/json' \ -d '{"program":"acme_partners","display_name":"Acme partner programme","commission_kind":"percent","commission_rate":20,"recurring":"first_only","cookie_days":90,"hold_days":30,"min_payout_cents":10000}' ``` Returns: ```json { "ok": true, "program": "acme_partners", "commission_rate": 20, "hold_days": 30 } ``` #### refer.upsert_partner `POST https://boldstudios.io/api/v1/refer/partners` Scope `refer:write`. free. Create or update a partner and get their link Creates the partner and their shareable link in one call, because a partner without a code cannot refer anybody and that half-finished state is how referral systems end up with codes and no conversions. Give a contact address and only its SHA-256 is stored: enough to reconcile against, useless if the table ever leaks. ```bash curl -X POST 'https://boldstudios.io/api/v1/refer/partners' \ -H "Authorization: Bearer $BOLD_API_KEY" \ -H 'Content-Type: application/json' \ -d '{"program":"acme_partners","partner_ref":"u_8814","display_name":"Jane Doe","payout_method":"bold_wallet"}' ``` Returns: ```json { "ok": true, "partner": { "partner_ref": "u_8814", "display_name": "Jane Doe", "status": "active", "payout_method": "bold_wallet" }, "codes": [ { "code": "K7M2QW", "destination_url": "https://acme.example.com" } ], "links": [ "https://boldstudios.io/refer/r/K7M2QW" ] } ``` #### refer.conversion `POST https://boldstudios.io/api/v1/refer/conversions` Scope `refer:write`. 10 credits ($0.01) per call. Record a sale Records a sale and works out what it earns: whose it is, at the rate frozen when the referral was made, and whether the programme pays on this one at all. Idempotent on (program, external_ref, kind), because the caller is normally a payment webhook and repeat delivery is routine rather than a fault. A replay comes back flagged as a replay and charges nothing twice. A sale with nothing linking it to a partner comes back attributed false with a reason, not an error. ```bash curl -X POST 'https://boldstudios.io/api/v1/refer/conversions' \ -H "Authorization: Bearer $BOLD_API_KEY" \ -H 'Content-Type: application/json' \ -d '{"program":"acme_partners","external_ref":"inv_10241","kind":"sale","subject_ref":"cus_5512","amount_cents":500000,"visit_token":"v_9c1f7b2e5d0a4318bc6e91f207d54a83"}' ``` Returns: ```json { "ok": true, "attributed": true, "conversion": { "external_ref": "inv_10241", "amount_cents": 500000, "commission_cents": 100000, "rate_applied": 20, "status": "pending", "hold_until": "2026-10-03T13:02:07Z" } } ``` #### refer.partner_summary `GET https://boldstudios.io/api/v1/refer/partners/{partner_ref}` Scope `refer:read`. free. One partner's numbers Clicks, people referred, earnings split into pending, approved, paid and rejected, and their last 25 conversions. This is what you render on a partner's own dashboard. ```bash curl 'https://boldstudios.io/api/v1/refer/partners/{partner_ref}?program=acme_partners' -H "Authorization: Bearer $BOLD_API_KEY" ``` Returns: ```json { "ok": true, "partner_ref": "u_8814", "clicks": 412, "referred": 26, "earnings": { "pending_cents": 100000, "approved_cents": 0, "paid_cents": 240000, "rejected_cents": 0 } } ``` #### refer.payouts_due `GET https://boldstudios.io/api/v1/refer/payouts/due` Scope `refer:read`. free. What is owed and to whom Every partner with approved commission that has not been paid, with the total and whether it has cleared the programme's minimum. Approved means the hold period has passed and nothing reversed it. ```bash curl 'https://boldstudios.io/api/v1/refer/payouts/due?program=acme_partners' -H "Authorization: Bearer $BOLD_API_KEY" ``` Returns: ```json { "ok": true, "due": [ { "program": "acme_partners", "partner_ref": "u_8814", "conversions": 1, "amount_cents": 100000, "min_payout_cents": 10000, "ready": true } ] } ``` #### refer.stats `GET https://boldstudios.io/api/v1/refer/stats` Scope `refer:read`. free. Programme totals Clicks, people referred, conversions, revenue attributed, commission earned and commission still owed, across your whole property. ```bash curl 'https://boldstudios.io/api/v1/refer/stats?days=30' -H "Authorization: Bearer $BOLD_API_KEY" ``` Returns: ```json { "ok": true, "property": "acme", "stats": { "from": "2026-08-05", "to": "2026-09-03", "programs": 1, "partners": 12, "clicks": 3140, "referred": 208, "conversions": 14, "revenue_cents": 4100000, "commission_cents": 820000, "owed_cents": 300000, "unified": { "people": 412, "people_link_only": 150, "people_giveaway_only": 204, "people_both": 58, "people_if_counted_separately": 470, "duplicates_removed": 58, "touches": { "referral_link": 208, "giveaway_entry": 262, "giveaway_share": 41 }, "conversions": 14, "credited_by": { "referral_link": 9, "giveaway_share": 3, "direct": 2, "recorded": 0 }, "giveaway_sourced_commission_cents": 150000, "commission_cents": 820000 } } } ``` ### BOLD Sign Send a document for signature and get back links you can deliver however you like. Dropbox Sign charges $75 a month for about fifty of these. #### sign.create_document `POST https://boldstudios.io/api/v1/sign/documents` Scope `sign:write`. 100 credits ($0.10) per envelope. Send a document for signature Give it a title, a publicly reachable URL to the document, and the people who need to sign. You get back a signing URL for each of them. BOLD does not email your signers: you get the links and send them the way you already send things, which keeps your recipients on your sending reputation rather than ours. Charged once per envelope, however many signers are on it. ```bash curl -X POST 'https://boldstudios.io/api/v1/sign/documents' \ -H "Authorization: Bearer $BOLD_API_KEY" \ -H 'Content-Type: application/json' \ -d '{"title":"Photography services agreement","source_url":"https://example.com/agreement.pdf","signers":[{"email":"dana@example.com","name":"Dana Whitfield"}]}' ``` Returns: ```json { "document_id": "0d2a5c81-7f34-4b96-a0e2-6d8c1b49f537", "title": "Photography services agreement", "status": "sent", "expires_at": "2026-10-02T18:00:00Z", "signers": [ { "signer_id": "77c1e408-9d52-4a37-b6f1-24e0a9c73d85", "display_name": "Dana Whitfield", "role": "signer", "order": 0, "status": "pending", "signing_url": "https://boldstudios.io/sign/9f3ca7d21e6b48059c3fa1287d6e40b5" } ] } ``` #### sign.list_documents `GET https://boldstudios.io/api/v1/sign/documents` Scope `sign:read`. free. List your signature requests Your documents, newest first, with how many signers have signed. Free. ```bash curl 'https://boldstudios.io/api/v1/sign/documents?limit=25' -H "Authorization: Bearer $BOLD_API_KEY" ``` Returns: ```json { "documents": [ { "document_id": "0d2a5c81-7f34-4b96-a0e2-6d8c1b49f537", "title": "Photography services agreement", "status": "completed", "signers": 2, "signed": 2 } ] } ``` #### sign.get_document `GET https://boldstudios.io/api/v1/sign/documents/{id}` Scope `sign:read`. free. Check a signature request Status of the document and of each signer. Returns display names and states, never the email addresses you supplied and never a signing token, so a stolen key cannot be turned into a list of the people involved. Free. ```bash curl 'https://boldstudios.io/api/v1/sign/documents/{id}' -H "Authorization: Bearer $BOLD_API_KEY" ``` Returns: ```json { "document_id": "0d2a5c81-7f34-4b96-a0e2-6d8c1b49f537", "title": "Photography services agreement", "status": "completed", "completed_at": "2026-09-03T09:14:00Z", "signers": [ { "signer_id": "77c1e408-9d52-4a37-b6f1-24e0a9c73d85", "display_name": "Dana Whitfield", "status": "signed", "signed_at": "2026-09-03T09:14:00Z", "signature_type": "typed" } ] } ``` #### sign.audit_trail `GET https://boldstudios.io/api/v1/sign/documents/{id}/audit` Scope `sign:read`. free. The audit trail Every event on the document with its timestamp, the signer it belongs to, and the IP and user agent it came from. This is the record a dispute asks for, and it is the only read in the whole BOLD API that returns an address. It is reachable only by the account that sent the document. ```bash curl 'https://boldstudios.io/api/v1/sign/documents/{id}/audit' -H "Authorization: Bearer $BOLD_API_KEY" ``` Returns: ```json { "document_id": "0d2a5c81-7f34-4b96-a0e2-6d8c1b49f537", "status": "completed", "events": [ { "action": "document_created", "at": "2026-09-02T18:00:00Z", "actor": "sender" }, { "action": "signer_viewed", "at": "2026-09-03T09:11:00Z", "actor": "dana@example.com", "ip": "203.0.113.42", "user_agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36" }, { "action": "consent_given", "at": "2026-09-03T09:14:00Z", "actor": "dana@example.com", "ip": "203.0.113.42" }, { "action": "signer_signed", "at": "2026-09-03T09:14:00Z", "actor": "dana@example.com", "ip": "203.0.113.42" }, { "action": "document_completed", "at": "2026-09-03T09:14:00Z", "actor": "sender" } ] } ``` #### sign.void_document `POST https://boldstudios.io/api/v1/sign/documents/{id}/void` Scope `sign:write`. free. Void a signature request Stops a document that has not completed. Anyone opening a signing link afterwards is told it was withdrawn. Free. ```bash curl -X POST 'https://boldstudios.io/api/v1/sign/documents/{id}/void' \ -H "Authorization: Bearer $BOLD_API_KEY" \ -H 'Content-Type: application/json' \ -d '{"reason":"Terms renegotiated"}' ``` Returns: ```json { "document_id": "0d2a5c81-7f34-4b96-a0e2-6d8c1b49f537", "status": "voided", "voided_at": "2026-09-02T19:00:00Z" } ``` ### BOLD Calendar Read and write a calendar, ask when somebody is free, and take a booking. The free/busy maths already carves shifts out of availability, so a booking can never land on time that is already spoken for. #### calendar.list `GET https://boldstudios.io/api/v1/calendar/calendars` Scope `calendar:read`. free. Every calendar you can see Calendars you own and calendars somebody shared with you, newest ownership first. personal marks the one BOLD made for your own week; it is not counted against your plan. Each calendar also lists its active booking types, whose ids are what calendar.slots and calendar.book take. No worked example yet. `GET https://boldstudios.io/api/v1` carries the current shape. #### calendar.create `POST https://boldstudios.io/api/v1/calendar/calendars` Scope `calendar:write`. 20 credits ($0.02) per calendar. Make a calendar Creates an empty calendar you own, with you on it as an admin. Timezone matters: every time you send afterwards is read as wall clock in this zone, which is what keeps "9am Tuesday" meaning that through a daylight saving change. No worked example yet. `GET https://boldstudios.io/api/v1` carries the current shape. #### calendar.list_events `GET https://boldstudios.io/api/v1/calendar/calendars/{calendar_id}/events` Scope `calendar:read`. free. What is on a calendar Entries between two dates, oldest first, in the calendar's own timezone. Absent dates read the next sixty days. A range longer than a year is clamped rather than refused, so one call can never walk a whole history. No worked example yet. `GET https://boldstudios.io/api/v1` carries the current shape. #### calendar.create_event `POST https://boldstudios.io/api/v1/calendar/calendars/{calendar_id}/events` Scope `calendar:write`. 10 credits ($0.01) per entry. Put something on a day Adds one entry. Times are wall clock in the calendar's zone, not instants. This never opens the entry for volunteers to sign up for: that is a different act with a different shape, and defaulting to it would attach every job on the calendar to whatever you just posted. No worked example yet. `GET https://boldstudios.io/api/v1` carries the current shape. #### calendar.slots `GET https://boldstudios.io/api/v1/calendar/booking-types/{booking_type_id}/slots` Scope `calendar:read`. free. When you are free to be booked Open start times for one booking type, day by day, as absolute instants. Availability windows, the notice period, the daily cap, the gap after a meeting, bookings already taken and any shift on the calendar are all applied before you see this, so anything returned here is genuinely bookable. No worked example yet. `GET https://boldstudios.io/api/v1` carries the current shape. #### calendar.book `POST https://boldstudios.io/api/v1/calendar/booking-types/{booking_type_id}/bookings` Scope `calendar:write`. 50 credits ($0.05) per booking. Take a booking Books one of the open times. Two people racing for the same slot is settled in the database, not in your code, so the loser gets slot_gone rather than a double booking. The manage_token comes back exactly once and is the booker's only way to change or cancel it later, so pass it on. No worked example yet. `GET https://boldstudios.io/api/v1` carries the current shape. #### calendar.list_bookings `GET https://boldstudios.io/api/v1/calendar/bookings` Scope `calendar:read`. free. Bookings on your calendars Upcoming bookings across every calendar you can see, soonest first. Defaults to the next ninety days. No worked example yet. `GET https://boldstudios.io/api/v1` carries the current shape. #### calendar.cancel_booking `POST https://boldstudios.io/api/v1/calendar/bookings/{booking_id}/cancel` Scope `calendar:write`. free. Cancel a booking Cancels a booking on one of your calendars and emails the person who made it. Cancelling somebody's meeting and letting them turn up anyway is worse than not offering cancellation at all, so the email is not optional. Cancelling an already cancelled booking is not an error. No worked example yet. `GET https://boldstudios.io/api/v1` carries the current shape. ### BOLD Send Email to your own list from your own verified domain. Contacts, lists, suppressions, sender domains and broadcast drafts, with deliverability machinery underneath that measures whether an open was real and refuses a send that would cost you your reputation. #### send.list_brands `GET https://boldstudios.io/api/v1/send/brands` Scope `send:read`. free. List the brands this key can send from BOLD Send is brand scoped and an API key is account scoped, so this is the first call to make. It returns every brand this key may act on, owned or invited, with the brand_id every other send operation needs. If your account has exactly one brand you may leave brand_id out of the paths below and it will be resolved for you. ```bash curl 'https://boldstudios.io/api/v1/send/brands' -H "Authorization: Bearer $BOLD_API_KEY" ``` Returns: ```json { "brands": [ { "brand_id": "7263381e-0611-491a-8672-72fbdf22d6e4", "slug": "acme", "name": "Acme", "mine": true, "default": true, "from_name": "Acme", "from_email": "hello@acme.com", "reply_to": "support@acme.com", "contacts": 4820, "lists": 6, "verified_domains": 1 } ] } ``` #### send.list_domains `GET https://boldstudios.io/api/v1/send/brands/{brand_id}/domains` Scope `send:read`. free. List sender domains and whether they can send Every verified sender domain on this brand, with the provider that verified it. A domain verified with one provider cannot be sent through the other, so when a send is refused for a domain that looks fine, this is the call that says why. ```bash curl 'https://boldstudios.io/api/v1/send/brands/{brand_id}/domains' -H "Authorization: Bearer $BOLD_API_KEY" ``` Returns: ```json { "brand_id": "7263381e-0611-491a-8672-72fbdf22d6e4", "domains": [ { "domain": "acme.com", "status": "verified", "provider": "ses", "can_send": true, "created_at": "2026-04-02T10:00:00Z" }, { "domain": "mail.acme.io", "status": "pending", "provider": "resend", "can_send": false, "created_at": "2026-09-01T09:30:00Z" } ] } ``` #### send.list_contacts `GET https://boldstudios.io/api/v1/send/brands/{brand_id}/contacts` Scope `send:read`. free. List contacts on a brand Paged, newest first. Filter with status (subscribed, unsubscribed, bounced, invalid) and search on a substring of the address. The equivalent of a Mailchimp audience members read. ```bash curl 'https://boldstudios.io/api/v1/send/brands/{brand_id}/contacts?status=subscribed&limit=2' -H "Authorization: Bearer $BOLD_API_KEY" ``` Returns: ```json { "brand_id": "7263381e-0611-491a-8672-72fbdf22d6e4", "total": 4820, "limit": 2, "offset": 0, "contacts": [ { "contact_id": "b1f0c0de-0000-4000-8000-000000000001", "email": "dana@example.com", "first_name": "Dana", "last_name": "Okafor", "status": "subscribed", "consent": true, "source": "api", "fields": { "plan": "pro" }, "created_at": "2026-09-10T12:00:00Z" } ] } ``` #### send.upsert_contact `POST https://boldstudios.io/api/v1/send/brands/{brand_id}/contacts` Scope `send:write`. free. Create or update one contact by email Idempotent on the address, so an import can be replayed without duplicating anybody. Custom fields are merged rather than replaced. Free, because moving a list you already own onto BOLD should not cost per person. ```bash curl -X POST 'https://boldstudios.io/api/v1/send/brands/{brand_id}/contacts' \ -H "Authorization: Bearer $BOLD_API_KEY" \ -H 'Content-Type: application/json' \ -d '{"email":"dana@example.com","first_name":"Dana","last_name":"Okafor","fields":{"plan":"pro"}}' ``` Returns: ```json { "contact_id": "b1f0c0de-0000-4000-8000-000000000001", "brand_id": "7263381e-0611-491a-8672-72fbdf22d6e4", "email": "dana@example.com", "first_name": "Dana", "last_name": "Okafor", "status": "subscribed", "consent": true, "fields": { "plan": "pro" }, "created": true } ``` #### send.delete_contact `POST https://boldstudios.io/api/v1/send/brands/{brand_id}/contacts/{contact_id}/delete` Scope `send:write`. free. Delete one contact outright A hard delete, for an erasure request. To stop mailing somebody while keeping the record, suppress the address instead. ```bash curl 'https://boldstudios.io/api/v1/send/brands/{brand_id}/contacts/{contact_id}/delete' -H "Authorization: Bearer $BOLD_API_KEY" ``` Returns: ```json { "deleted": true, "contact_id": "b1f0c0de-0000-4000-8000-000000000001", "brand_id": "7263381e-0611-491a-8672-72fbdf22d6e4" } ``` #### send.list_lists `GET https://boldstudios.io/api/v1/send/brands/{brand_id}/lists` Scope `send:read`. free. List the audiences on a brand Every list on this brand with its live member count. ```bash curl 'https://boldstudios.io/api/v1/send/brands/{brand_id}/lists' -H "Authorization: Bearer $BOLD_API_KEY" ``` Returns: ```json { "brand_id": "7263381e-0611-491a-8672-72fbdf22d6e4", "lists": [ { "list_id": "c2e1d1ef-0000-4000-8000-000000000002", "name": "Customers", "description": "Everyone with an active plan", "members": 1204, "created_at": "2026-05-01T08:00:00Z" } ] } ``` #### send.create_list `POST https://boldstudios.io/api/v1/send/brands/{brand_id}/lists` Scope `send:write`. 20 credits ($0.02) per list. Create an audience A named list you can then add contacts to and point a broadcast at. ```bash curl -X POST 'https://boldstudios.io/api/v1/send/brands/{brand_id}/lists' \ -H "Authorization: Bearer $BOLD_API_KEY" \ -H 'Content-Type: application/json' \ -d '{"name":"Launch waitlist","description":"Signed up before launch day"}' ``` Returns: ```json { "list_id": "c2e1d1ef-0000-4000-8000-000000000002", "brand_id": "7263381e-0611-491a-8672-72fbdf22d6e4", "name": "Launch waitlist", "members": 0 } ``` #### send.update_list_members `POST https://boldstudios.io/api/v1/send/brands/{brand_id}/lists/{list_id}/members` Scope `send:write`. free. Add and remove list members by email Send add and remove together, up to 500 addresses each. Addresses that are not contacts on this brand come back in unknown rather than being dropped silently, so a sync can tell you what it could not do. ```bash curl -X POST 'https://boldstudios.io/api/v1/send/brands/{brand_id}/lists/{list_id}/members' \ -H "Authorization: Bearer $BOLD_API_KEY" \ -H 'Content-Type: application/json' \ -d '{"add":["dana@example.com","sam@example.com"],"remove":["old@example.com"]}' ``` Returns: ```json { "list_id": "c2e1d1ef-0000-4000-8000-000000000002", "brand_id": "7263381e-0611-491a-8672-72fbdf22d6e4", "added": 1, "removed": 1, "unknown": [ "sam@example.com" ], "members": 1204 } ``` #### send.list_suppressions `GET https://boldstudios.io/api/v1/send/brands/{brand_id}/suppressions` Scope `send:read`. free. List suppressed addresses Everything this brand will refuse to mail, with the reason. Account wide legacy suppressions are enforced on every send and are not listed here. ```bash curl 'https://boldstudios.io/api/v1/send/brands/{brand_id}/suppressions' -H "Authorization: Bearer $BOLD_API_KEY" ``` Returns: ```json { "brand_id": "7263381e-0611-491a-8672-72fbdf22d6e4", "total": 312, "limit": 50, "offset": 0, "note": "Brand scoped suppressions. Account wide legacy suppressions are still enforced on every send and are not listed here.", "suppressions": [ { "email": "left@example.com", "reason": "unsubscribe", "detail": null, "created_at": "2026-09-12T18:20:00Z" } ] } ``` #### send.suppress `POST https://boldstudios.io/api/v1/send/brands/{brand_id}/suppressions` Scope `send:write`. free. Suppress an address Stops every send to this address on this brand, campaign and transactional alike. An unsubscribe also marks the contact unsubscribed so the dashboard and the suppression list agree. Always free. ```bash curl -X POST 'https://boldstudios.io/api/v1/send/brands/{brand_id}/suppressions' \ -H "Authorization: Bearer $BOLD_API_KEY" \ -H 'Content-Type: application/json' \ -d '{"email":"left@example.com","reason":"unsubscribe"}' ``` Returns: ```json { "suppressed": true, "brand_id": "7263381e-0611-491a-8672-72fbdf22d6e4", "email": "left@example.com", "reason": "unsubscribe" } ``` #### send.list_campaigns `GET https://boldstudios.io/api/v1/send/brands/{brand_id}/campaigns` Scope `send:read`. free. List broadcasts with their results Every campaign on this brand with recipients, sends, deliveries, opens, clicks, bounces, complaints and unsubscribes. opened is every open; human_opened excludes the machine opens a scanner generates, which is the number worth acting on. ```bash curl 'https://boldstudios.io/api/v1/send/brands/{brand_id}/campaigns' -H "Authorization: Bearer $BOLD_API_KEY" ``` Returns: ```json { "brand_id": "7263381e-0611-491a-8672-72fbdf22d6e4", "campaigns": [ { "campaign_id": "d3a2e2f0-0000-4000-8000-000000000003", "name": "September update", "subject": "What shipped in September", "status": "sent", "from_name": "Acme", "from_email": "hello@acme.com", "list_id": "c2e1d1ef-0000-4000-8000-000000000002", "scheduled_at": null, "sent_at": "2026-09-14T15:00:00Z", "created_at": "2026-09-13T11:00:00Z", "stats": { "recipients": 1204, "sent": 1204, "delivered": 1189, "opened": 486, "human_opened": 331, "clicked": 92, "bounced": 15, "complained": 0, "unsubscribed": 4, "suppressed": 0 } } ] } ``` #### send.create_campaign `POST https://boldstudios.io/api/v1/send/brands/{brand_id}/campaigns` Scope `send:write`. 50 credits ($0.05) per campaign. Create a broadcast as a draft Creates the campaign and nothing more. It is a draft with no schedule, and it sends nothing until a person starts it in BOLD Send. This call bills the BOLD wallet in cents for creating it and spends no BOLD Send credits, because no message leaves. ```bash curl -X POST 'https://boldstudios.io/api/v1/send/brands/{brand_id}/campaigns' \ -H "Authorization: Bearer $BOLD_API_KEY" \ -H 'Content-Type: application/json' \ -d '{"name":"October update","subject":"What shipped in October","html":"

Hello

Here is what changed.

","from_name":"Acme","from_email":"hello@acme.com","list_id":"c2e1d1ef-0000-4000-8000-000000000002"}' ``` Returns: ```json { "campaign_id": "d3a2e2f0-0000-4000-8000-000000000004", "brand_id": "7263381e-0611-491a-8672-72fbdf22d6e4", "status": "draft", "subject": "What shipped in October", "list_id": "c2e1d1ef-0000-4000-8000-000000000002", "note": "Created as a draft. Nothing is sent and no send credits are spent until it is started in BOLD Send, which is where the sending balance lives." } ``` #### send.get_email `GET https://boldstudios.io/api/v1/send/emails/{email_id}` Scope `send:read`. free. Look up one transactional send Status and the delivery, open, click, bounce and complaint timestamps for a single message, the counterpart of the same read on Resend. Messages are sent today through the BOLD Send transactional endpoint, which meters them against BOLD Send credits rather than against the BOLD wallet. ```bash curl 'https://boldstudios.io/api/v1/send/emails/{email_id}' -H "Authorization: Bearer $BOLD_API_KEY" ``` Returns: ```json { "email_id": "e4b3f3a1-0000-4000-8000-000000000005", "brand_id": "7263381e-0611-491a-8672-72fbdf22d6e4", "to": "dana@example.com", "from": "receipts@acme.com", "subject": "Your receipt", "status": "sent", "provider": "ses", "error": null, "tags": { "kind": "receipt" }, "created_at": "2026-09-16T09:00:00Z", "sent_at": "2026-09-16T09:00:01Z", "delivered_at": "2026-09-16T09:00:04Z", "opened_at": null, "clicked_at": null, "bounced_at": null, "complained_at": null } ``` #### send.domain_health `GET https://boldstudios.io/api/v1/send/brands/{brand_id}/domains/health` Scope `send:read`. free. Where each domain stands and what is left to do For each organisational domain on the brand (and your own BOLD Domains): overall state (ready, working, needs_you, blocked, or not_synced), the next step in plain words, who hosts its DNS and whether BOLD can write it, Amazon SES, DMARC and Google Postmaster state, the exact DNS records still missing, and the one click Domain Connect link or nameserver move when one exists. Read only. ```bash curl 'https://boldstudios.io/api/v1/send/brands/{brand_id}/domains/health' -H "Authorization: Bearer $BOLD_API_KEY" ``` Returns: ```json { "brand_id": "7263381e-0611-491a-8672-72fbdf22d6e4", "domains": [ { "domain": "acme.com", "overall": "needs_you", "next_step": "Add 2 records at GoDaddy. BOLD keeps checking and finishes the rest on its own.", "dns": { "host": "godaddy", "host_label": "GoDaddy", "writable": false, "manage_url": "https://dcc.godaddy.com/control/dnsmanagement?domainName=acme.com", "nameservers": [ "ns1.domaincontrol.com", "ns2.domaincontrol.com" ] }, "sending": { "domains": [ "mail.acme.com" ], "ses_ok": false, "detail": [ { "domain": "mail.acme.com", "verified": false, "dkim": "PENDING", "mail_from": null, "error": null } ] }, "dmarc": { "ok": true, "record": "v=DMARC1; p=none; adkim=r; aspf=r" }, "postmaster": "added", "records_needed": [ { "type": "CNAME", "name": "abc123._domainkey.mail.acme.com", "value": "abc123.dkim.amazonses.com", "purpose": "dkim" }, { "type": "TXT", "name": "acme.com", "value": "google-site-verification=xyz", "purpose": "postmaster" } ], "connect": { "domain_connect_url": null, "domain_connect_provider": null, "move_status": null, "move_nameservers": null, "credential_provider": null }, "last_synced_at": "2026-10-03T13:00:12Z", "next_check_at": "2026-10-03T14:00:12Z" } ] } ``` #### send.sync_domains `POST https://boldstudios.io/api/v1/send/brands/{brand_id}/domains/sync` Scope `send:write`. free. Re-check a domain now, and optionally turn on sending Runs the domain automation now instead of waiting for the hourly pass: reads DNS, writes any missing records BOLD can write, checks SES and Postmaster, and returns the refreshed health rows. Name one domain, or leave it out to check every domain on the brand (at most 25). enable_sending true also creates the mail. SES identity for a domain that has none. ```bash curl -X POST 'https://boldstudios.io/api/v1/send/brands/{brand_id}/domains/sync' \ -H "Authorization: Bearer $BOLD_API_KEY" \ -H 'Content-Type: application/json' \ -d '{"domain":"acme.com","enable_sending":true}' ``` Returns: ```json { "brand_id": "7263381e-0611-491a-8672-72fbdf22d6e4", "enable_sending": true, "checked": [ { "domain": "acme.com", "overall": "working" } ], "queued": [], "skipped_over_limit": 0, "domains": [ { "domain": "acme.com", "overall": "working", "next_step": "BOLD added 3 records at Cloudflare. They go live within minutes; BOLD verifies them automatically.", "records_needed": [] } ] } ``` ### BOLD Mail Hosted inboxes on your own domain, for people and for agents. List mailboxes, create one, read the inbox, reply in thread and send, all against the mailbox's own sending allowance. #### mail.list_mailboxes `GET https://boldstudios.io/api/v1/mail/mailboxes` Scope `mail:read`. free. Your mailboxes Every BOLD Mail mailbox on brands you can edit, or on one brand, with its address, kind (person, agent or shared), unread count and how much of its hourly and daily sending allowance is used. ```bash curl 'https://boldstudios.io/api/v1/mail/mailboxes' -H "Authorization: Bearer $BOLD_API_KEY" ``` Returns: ```json { "brand_id": null, "mailboxes": [ { "mailbox_id": "5b0e7a52-0000-4000-8000-000000000010", "brand_id": "7263381e-0611-491a-8672-72fbdf22d6e4", "address": "support@acme.com", "display_name": "Acme Support", "kind": "agent", "mailbox_type": "mailbox", "status": "active", "can_send": true, "unread": 3, "limits": { "per_hour": 100, "per_day": 500, "used_last_hour": 4, "used_last_day": 37 }, "created_at": "2026-10-01T09:00:00Z" } ] } ``` #### mail.create_mailbox `POST https://boldstudios.io/api/v1/mail/mailboxes` Scope `mail:write`. A seat on your BOLD Mail plan; up to 10 mailboxes without a subscription. Create a mailbox Creates local_part@domain on a brand. The domain must be verified for that brand and enabled for receiving in BOLD Mail. Role addresses such as postmaster or abuse need confirm_reserved true. kind agent marks an inbox an automated agent reads and answers; every kind can send and receive. Billed as a BOLD Mail seat by the brand's BOLD Mail subscription, not by this call. Needs owner or manager on the brand. ```bash curl -X POST 'https://boldstudios.io/api/v1/mail/mailboxes' \ -H "Authorization: Bearer $BOLD_API_KEY" \ -H 'Content-Type: application/json' \ -d '{"brand_id":"7263381e-0611-491a-8672-72fbdf22d6e4","local_part":"support","domain":"acme.com","display_name":"Acme Support","kind":"agent"}' ``` Returns: ```json { "mailbox_id": "5b0e7a52-0000-4000-8000-000000000010", "brand_id": "7263381e-0611-491a-8672-72fbdf22d6e4", "address": "support@acme.com", "display_name": "Acme Support", "kind": "agent", "mailbox_type": "mailbox", "status": "active", "billable": true, "monthly_cost_usd": 4, "seats_used": 3, "seats_allowed": 10 } ``` #### mail.list_messages `GET https://boldstudios.io/api/v1/mail/mailboxes/{mailbox_id}/messages` Scope `mail:read`. free. Read a folder, newest first Messages in one folder of a mailbox, newest first, with sender, recipients, subject, snippet and flags. q is a full text search over subject, sender and body. Page with next_before. ```bash curl 'https://boldstudios.io/api/v1/mail/mailboxes/{mailbox_id}/messages?folder=inbox&limit=1' -H "Authorization: Bearer $BOLD_API_KEY" ``` Returns: ```json { "mailbox_id": "5b0e7a52-0000-4000-8000-000000000010", "folder": "inbox", "limit": 1, "has_more": true, "next_before": "2026-10-03T14:02:11.482113Z_6c1d2e3f-0000-4000-8000-000000000020", "messages": [ { "message_id": "6c1d2e3f-0000-4000-8000-000000000020", "thread_id": "7d2e3f40-0000-4000-8000-000000000030", "folder": "inbox", "direction": "in", "from": { "email": "dana@example.com", "name": "Dana Okafor" }, "to": [ { "email": "support@acme.com" } ], "cc": [], "subject": "Where is my order?", "snippet": "Hi, I ordered on Monday and have not had a shipping email yet.", "is_read": false, "is_starred": false, "has_attachments": false, "received_at": "2026-10-03T14:02:10Z", "sent_at": null, "created_at": "2026-10-03T14:02:11.482113Z" } ] } ``` #### mail.get_message `GET https://boldstudios.io/api/v1/mail/messages/{message_id}` Scope `mail:read`. free. Read one message One message in full: text and HTML bodies, every recipient, the thread it belongs to, its RFC Message-ID, SPF, DKIM, DMARC, spam and virus verdicts, and attachment names and sizes. Does not mark it read. ```bash curl 'https://boldstudios.io/api/v1/mail/messages/{message_id}' -H "Authorization: Bearer $BOLD_API_KEY" ``` Returns: ```json { "message_id": "6c1d2e3f-0000-4000-8000-000000000020", "mailbox_id": "5b0e7a52-0000-4000-8000-000000000010", "thread_id": "7d2e3f40-0000-4000-8000-000000000030", "folder": "inbox", "direction": "in", "from": { "email": "dana@example.com", "name": "Dana Okafor" }, "to": [ { "email": "support@acme.com" } ], "cc": [], "bcc": [], "reply_to": null, "subject": "Where is my order?", "body_text": "Hi, I ordered on Monday and have not had a shipping email yet. Order 1042.", "body_html": "

Hi, I ordered on Monday and have not had a shipping email yet. Order 1042.

", "is_read": false, "is_starred": false, "has_attachments": false, "rfc_message_id": "", "in_reply_to": null, "references": [], "auth": { "spf": "PASS", "dkim": "PASS", "dmarc": "PASS", "spam": "PASS", "virus": "PASS" }, "attachments": [], "received_at": "2026-10-03T14:02:10Z", "sent_at": null, "created_at": "2026-10-03T14:02:11.482113Z" } ``` #### mail.send `POST https://boldstudios.io/api/v1/mail/mailboxes/{mailbox_id}/send` Scope `mail:write`. Draws on the mailbox's hourly and daily sending limits in your BOLD Mail plan. Send or reply from a mailbox Sends a message from the mailbox's own address through Amazon SES, exactly as the BOLD Mail inbox does: the hourly and daily recipient limits apply, in_reply_to threads it under the message being answered, and a copy lands in the Sent folder. Counts against the mailbox's sending allowance, not your BOLD balance. Send an Idempotency-Key header (or idempotency_key) and a retry returns the first result instead of sending again. ```bash curl -X POST 'https://boldstudios.io/api/v1/mail/mailboxes/{mailbox_id}/send' \ -H "Authorization: Bearer $BOLD_API_KEY" \ -H 'Content-Type: application/json' \ -d '{"to":["dana@example.com"],"subject":"","text":"Hi Dana, order 1042 shipped this morning. Tracking: 1Z999AA10123456784.","in_reply_to":"6c1d2e3f-0000-4000-8000-000000000020"}' ``` Returns: ```json { "sent": true, "replayed": false, "mailbox_id": "5b0e7a52-0000-4000-8000-000000000010", "message_id": "8e3f4051-0000-4000-8000-000000000040", "thread_id": "7d2e3f40-0000-4000-8000-000000000030", "rfc_message_id": "<3f9b1c2e-0000-4000-8000-000000000050@acme.com>", "recipients": 1, "warnings": [] } ``` #### mail.mark `POST https://boldstudios.io/api/v1/mail/messages/{message_id}/mark` Scope `mail:write`. free. Mark read, star, or move Sets read, starred and folder on one message. Received mail can move between inbox, archive, spam and trash; sent mail between sent, archive and trash. Drafts cannot be marked read or moved. Repeating the call changes nothing further. ```bash curl -X POST 'https://boldstudios.io/api/v1/mail/messages/{message_id}/mark' \ -H "Authorization: Bearer $BOLD_API_KEY" \ -H 'Content-Type: application/json' \ -d '{"read":true,"folder":"archive"}' ``` Returns: ```json { "message_id": "6c1d2e3f-0000-4000-8000-000000000020", "thread_id": "7d2e3f40-0000-4000-8000-000000000030", "folder": "archive", "is_read": true, "is_starred": false } ``` ### BOLD Update One update, every channel a recipient chose: email with attachments, signed webhooks, Telegram, web push and in-app. #### update.topics `GET https://boldstudios.io/api/v1/update/projects/{project_id}/topics` Scope `update:read`. free. List the topics a project can publish to The BOLD built-in topics plus any this project defined for itself. Recipients subscribe per topic, so these are the names to use when publishing and when setting someone up. ```bash curl 'https://boldstudios.io/api/v1/update/projects/{project_id}/topics' -H "Authorization: Bearer $BOLD_API_KEY" ``` Returns: ```json { "topics": [ { "topic": "deploy", "label": "Deploys", "description": "A new version of your site goes live, or fails.", "default_on": true, "custom": false }, { "topic": "report", "label": "Reports", "description": "Scheduled reports and their attachments.", "default_on": true, "custom": false }, { "topic": "new_episode", "label": "New episode", "description": "A new episode is published.", "default_on": true, "custom": true } ] } ``` #### update.publish `POST https://boldstudios.io/api/v1/update/projects/{project_id}/updates` Scope `update:write`. 10 credits ($0.01) per delivery. Publish an update to everyone subscribed to its topic Sends one update to every recipient subscribed to that topic, on whichever channels each of them chose. Attachments are carried into email as files. Priced per delivery rather than per call: an update that reaches nobody is free, and a repeated dedupe_key returns the existing update without queueing or charging anything. ```bash curl -X POST 'https://boldstudios.io/api/v1/update/projects/{project_id}/updates' \ -H "Authorization: Bearer $BOLD_API_KEY" \ -H 'Content-Type: application/json' \ -d '{"topic":"report","title":"September numbers","text":"Streams are up 14 percent on August. The full report is attached.","attachments":[{"filename":"September.pdf","url":"https://files.example.com/september.pdf"}],"dedupe_key":"report-2026-09"}' ``` Returns: ```json { "update_id": "0c7b1a92-3f5e-4d8a-9b21-6f0c4e2d7a13", "topic": "report", "deliveries": 12, "deduped": false } ``` #### update.list `GET https://boldstudios.io/api/v1/update/projects/{project_id}/updates` Scope `update:read`. free. List updates already published Newest first, each with a count of how many deliveries were sent, are pending, or failed. ```bash curl 'https://boldstudios.io/api/v1/update/projects/{project_id}/updates?limit=2' -H "Authorization: Bearer $BOLD_API_KEY" ``` Returns: ```json { "updates": [ { "update_id": "0c7b1a92-3f5e-4d8a-9b21-6f0c4e2d7a13", "topic": "report", "title": "September numbers", "text": "Streams are up 14 percent on August.", "attachments": [ { "filename": "September.pdf", "url": "https://files.example.com/september.pdf" } ], "source": "api", "created_at": "2026-09-19T11:04:22Z", "deliveries": { "total": 12, "sent": 12, "pending": 0, "failed": 0 } } ] } ``` #### update.status `GET https://boldstudios.io/api/v1/update/projects/{project_id}/updates/{update_id}` Scope `update:read`. free. Per-recipient delivery status for one update Every delivery the update produced, with its channel, status, attempt count and error if it failed. Recipient addresses are masked. ```bash curl 'https://boldstudios.io/api/v1/update/projects/{project_id}/updates/{update_id}' -H "Authorization: Bearer $BOLD_API_KEY" ``` Returns: ```json { "update_id": "0c7b1a92-3f5e-4d8a-9b21-6f0c4e2d7a13", "topic": "report", "title": "September numbers", "created_at": "2026-09-19T11:04:22Z", "deliveries": [ { "channel": "email", "masked": "o***r@hisglory.me", "status": "sent", "attempts": 1, "sent_at": "2026-09-19T11:05:01Z", "error": null }, { "channel": "webhook", "masked": "hooks.example.com", "status": "failed", "attempts": 5, "sent_at": null, "error": "endpoint answered 500" } ] } ``` #### update.subscribers `GET https://boldstudios.io/api/v1/update/projects/{project_id}/subscribers` Scope `update:read`. free. List who hears about this project Across every channel, including Telegram chats. Addresses are masked: an email reads o***r@example.com and a webhook reads as its host. ```bash curl 'https://boldstudios.io/api/v1/update/projects/{project_id}/subscribers' -H "Authorization: Bearer $BOLD_API_KEY" ``` Returns: ```json { "subscribers": [ { "subscriber_id": "8f2c1d40-9a33-4e62-b7d1-2c5e8a90b4f6", "channel": "email", "masked": "o***r@hisglory.me", "label": "Oscar", "topics": { "report": true, "deploy": false }, "subscribed": true, "verified": true, "created_at": "2026-09-02T04:00:29Z" }, { "subscriber_id": "1b93e7c5-52a8-4f19-8d60-3ae7c1f2b845", "channel": "telegram", "masked": "His Glory Music updates", "label": "His Glory Music updates", "topics": { "report": true }, "subscribed": true, "verified": true, "created_at": "2026-09-16T18:01:05Z" } ] } ``` #### update.subscribe `POST https://boldstudios.io/api/v1/update/projects/{project_id}/subscribers` Scope `update:write`. free. Add a recipient Adds an email, webhook or in-app recipient and sets which topics they receive. A webhook is returned a signing secret exactly once, on this call, and every POST to it afterwards carries an X-BOLD-Signature header of sha256=. Telegram is claimed with a deep link and web push is granted by a browser, so neither is added here. ```bash curl -X POST 'https://boldstudios.io/api/v1/update/projects/{project_id}/subscribers' \ -H "Authorization: Bearer $BOLD_API_KEY" \ -H 'Content-Type: application/json' \ -d '{"channel":"webhook","address":"https://hooks.example.com/bold","label":"Ops channel","topics":{"report":true,"deploy":true,"billing":false}}' ``` Returns: ```json { "subscriber_id": "1f4d8b60-2c19-47a5-9e83-5b0a7d3c6e21", "channel": "webhook", "masked": "hooks.example.com", "topics": { "report": true, "deploy": true, "billing": false }, "signing_secret": "b7c1e0a94f2d8536ab0c7e15d9f3428c6e0b5a1d7f94c2836ae05b17d3c9f402" } ``` #### update.unsubscribe `POST https://boldstudios.io/api/v1/update/projects/{project_id}/subscribers/{subscriber_id}/unsubscribe` Scope `update:write`. free. Stop sending to one recipient Unsubscribes rather than deletes, so the delivery history stays readable and re-adding the same address does not lose what it was told. ```bash curl 'https://boldstudios.io/api/v1/update/projects/{project_id}/subscribers/{subscriber_id}/unsubscribe' -H "Authorization: Bearer $BOLD_API_KEY" ``` Returns: ```json { "subscriber_id": "1f4d8b60-2c19-47a5-9e83-5b0a7d3c6e21", "subscribed": false, "found": true } ``` ### BOLD AI Chat, generation and transcription. #### ai.chat `POST https://boldstudios.io/api/v1/ai/chat` Scope `ai:chat`. 10 credits ($0.01) per 1k input tokens; per 1k output tokens 10 credits ($0.01) on flash, 20 credits ($0.02) on pro, 50 credits ($0.05) on max. A chat completion Charged in units of 1 cent: every 1,000 input tokens is one unit on every tier, and every 1,000 output tokens is 1 unit on flash, 2 on pro and 5 on max, rounded up, at least one unit a call. Send model flash, pro or max (or the exact model id one of them uses); other model names are refused. max_tokens is trimmed to what your balance can pay for, and the response says so in max_tokens_applied. The response's usage.billed_units is exactly what was charged. A failed or refused generation is never charged. ```bash curl -X POST 'https://boldstudios.io/api/v1/ai/chat' \ -H "Authorization: Bearer $BOLD_API_KEY" \ -H 'Content-Type: application/json' \ -d '{"model":"pro","max_tokens":512,"messages":[{"role":"user","content":"Write one line of launch copy for a coffee subscription."}]}' ``` Returns: ```json { "content": "Freshly roasted, on your counter before you run out.", "model": "claude-sonnet-5", "tier": "pro", "stop_reason": "end_turn", "usage": { "input_tokens": 18, "output_tokens": 12, "total_tokens": 30, "billed_units": 1 } } ``` #### ai.generate_image `POST https://boldstudios.io/api/v1/ai/images` Scope `ai:images`. 250 credits ($0.25) per image. Generate an image Returns a signed URL valid for 24 hours, stored in a private bucket. Standard is one unit, hd is four. The URL contains nothing that identifies the account it belongs to. ```bash curl -X POST 'https://boldstudios.io/api/v1/ai/images' \ -H "Authorization: Bearer $BOLD_API_KEY" \ -H 'Content-Type: application/json' \ -d '{"text":"A flat lay of a coffee bag on pale linen","style":"clean","quality":"standard","size":"1024x1024"}' ``` Returns: ```json { "url": "https://fdyhcokgnymevgtixwuf.supabase.co/storage/v1/object/sign/boldapi-media/1f8b0c2a-4d76-4e91-8b03-7c5a2e9d1046/9a4c7e13-2b58-4f60-a1d9-3e07c8b45f22.png?token=eyJhbGciOiJIUzI1NiJ9.example", "expires_in_seconds": 86400, "prompt": "the prompt actually sent to the model", "style": "clean", "quality": "standard", "size": "1024x1024" } ``` #### ai.transcribe `POST https://boldstudios.io/api/v1/ai/transcribe` Scope `ai:transcribe`. 50 credits ($0.05) per minute. Transcribe audio or video Give it a public audio or video URL. Waits up to 90 seconds for the result and returns it inline; if the file takes longer than that you get a job id and poll ai.transcript. Set wait_seconds to hold for less; anything above 90 is clamped to 90. Charged once, per minute of audio, whichever call first sees it finished. ```bash curl -X POST 'https://boldstudios.io/api/v1/ai/transcribe' \ -H "Authorization: Bearer $BOLD_API_KEY" \ -H 'Content-Type: application/json' \ -d '{"audio_url":"https://example.com/episode.mp3","language_code":"en","speaker_labels":false}' ``` Returns: ```json { "status": "completed", "id": "6f2c1b3d-8a45-4e12-9c07-5b3e2d1a9f04", "language": "en", "audio_duration_seconds": 214, "minutes_billed": 4, "word_count": 512, "text": "The full transcript, as one string." } ``` #### ai.transcript `GET https://boldstudios.io/api/v1/ai/transcribe/{id}` Scope `ai:transcribe`. 50 credits ($0.05) per minute. Fetch a transcript Poll a transcription started earlier. Charged once, per minute of audio, on the first response that is complete. Polling a job that is still running is free, and polling a finished one again is free. ```bash curl 'https://boldstudios.io/api/v1/ai/transcribe/{id}?words=true' -H "Authorization: Bearer $BOLD_API_KEY" ``` Returns: ```json { "status": "completed", "id": "6f2c1b3d-8a45-4e12-9c07-5b3e2d1a9f04", "audio_duration_seconds": 214, "minutes_billed": 4, "text": "The full transcript, as one string.", "words": [ { "text": "The", "start_ms": 120, "end_ms": 260, "confidence": 0.99 } ] } ``` ### BOLD Network Broadcast network operations for your own platforms: the programming guide and what is on now, scheduling the grid, the as-run log of what actually aired, audience by channel, platform, country and program, guest bookings and rundowns. #### network.list `GET https://boldstudios.io/api/v1/network/networks` Scope `network:read`. free. Your networks and channels Every BOLD Network you own or are a member of, with your role and capabilities, and each channel you can see: kind, public or private, whether it is on air, and its live HLS stream while it is. ```bash curl 'https://boldstudios.io/api/v1/network/networks' -H "Authorization: Bearer $BOLD_API_KEY" ``` Returns: ```json { "networks": [ { "network_id": "6b1f0c2e-8d3a-4f57-9e21-0a4c7d9b3e58", "slug": "his-glory", "name": "His Glory TV", "role": "Network manager", "capabilities": [ "channels.manage", "grid.edit", "guests.book", "traffic.view" ], "channels": [ { "channel_id": "2c7e9a14-5b3d-4e8f-a061-7d2b9c4e1f30", "slug": "main", "name": "His Glory 24/7", "kind": "linear", "is_public": true, "air_status": "on_air", "on_air_since": "2026-10-01T05:00:00Z", "hls_url": "https://media.boldhost.io/channels/2c7e9a14/kx81/live.m3u8" } ] } ] } ``` #### network.guide `GET https://boldstudios.io/api/v1/network/channels/{channel_id}/guide` Scope `network:read`. free. The programming guide for a channel Scheduled slots on one channel between from and to (default the next 24 hours, at most 15 days): kind, title, show, asset, live or not, status and the number of commercial breaks. Cancelled slots are left out unless include_cancelled is true. ```bash curl 'https://boldstudios.io/api/v1/network/channels/{channel_id}/guide?from=2026-10-04T00:00:00Z&to=2026-10-05T00:00:00Z' -H "Authorization: Bearer $BOLD_API_KEY" ``` Returns: ```json { "channel_id": "2c7e9a14-5b3d-4e8f-a061-7d2b9c4e1f30", "from": "2026-10-04T00:00:00Z", "to": "2026-10-05T00:00:00Z", "slots": [ { "slot_id": "91d3c5e7-2a4b-4c6d-8e0f-1a3b5c7d9e21", "starts_at": "2026-10-04T13:00:00Z", "ends_at": "2026-10-04T14:00:00Z", "duration_sec": 3600, "kind": "live", "title": "Take Five", "show_title": "Take Five", "live": true, "status": "scheduled", "breaks": 0 }, { "slot_id": "a2e4f6b8-3c5d-4e7f-9a1b-2c4d6e8f0a32", "starts_at": "2026-10-04T14:00:00Z", "ends_at": "2026-10-04T14:04:00Z", "duration_sec": 240, "kind": "break", "title": "Break", "live": false, "status": "scheduled", "breaks": 6 } ] } ``` #### network.now `GET https://boldstudios.io/api/v1/network/channels/{channel_id}/now` Scope `network:read`. free. What is on now and next The slot airing now and the next one on a channel, with the channel's air status (off, starting, on_air, stopping or error), when it went on air and its live HLS stream. ```bash curl 'https://boldstudios.io/api/v1/network/channels/{channel_id}/now' -H "Authorization: Bearer $BOLD_API_KEY" ``` Returns: ```json { "channel_id": "2c7e9a14-5b3d-4e8f-a061-7d2b9c4e1f30", "server_time": "2026-10-04T13:21:08Z", "air": { "status": "on_air", "on_air_since": "2026-10-01T05:00:00Z", "hls_url": "https://media.boldhost.io/channels/2c7e9a14/kx81/live.m3u8", "recoveries": 0 }, "now": { "slot_id": "91d3c5e7-2a4b-4c6d-8e0f-1a3b5c7d9e21", "kind": "live", "title": "Take Five", "starts_at": "2026-10-04T13:00:00Z", "ends_at": "2026-10-04T14:00:00Z", "live": true }, "next": { "slot_id": "a2e4f6b8-3c5d-4e7f-9a1b-2c4d6e8f0a32", "kind": "break", "title": "Break", "starts_at": "2026-10-04T14:00:00Z", "ends_at": "2026-10-04T14:04:00Z", "live": false } } ``` #### network.create_slot `POST https://boldstudios.io/api/v1/network/channels/{channel_id}/slots` Scope `network:write`. Free to schedule. A 24/7 channel bills 40 credits ($0.04) per minute on air. Schedule a slot on the grid Puts a program, live show, promo, commercial break or filler on a channel's grid. ends_at may be left out when an asset with a known length is given. A channel airs one thing at a time, so an overlap is refused with 409. Content whose rights window does not cover the slot is refused with 409 unless allow_uncleared is true. The 24/7 channel picks up the change within a minute. ```bash curl -X POST 'https://boldstudios.io/api/v1/network/channels/{channel_id}/slots' \ -H "Authorization: Bearer $BOLD_API_KEY" \ -H 'Content-Type: application/json' \ -d '{"starts_at":"2026-10-05T19:00:00Z","kind":"vod","asset_id":"c3f5a7b9-4d6e-4f80-a2b3-3d5e7f9a1b43","show_id":"d4a6b8c0-5e7f-4091-b3c4-4e6f8a0b2c54"}' ``` Returns: ```json { "slot_id": "e5b7c9d1-6f80-41a2-84d5-5f7a9b1c3d65", "channel_id": "2c7e9a14-5b3d-4e8f-a061-7d2b9c4e1f30", "starts_at": "2026-10-05T19:00:00Z", "ends_at": "2026-10-05T19:28:30Z", "duration_sec": 1710, "kind": "vod", "title": "Kingdom Stories", "show_title": "Kingdom Stories", "asset_title": "Kingdom Stories 104", "live": false, "status": "scheduled", "breaks": 0 } ``` #### network.cancel_slot `POST https://boldstudios.io/api/v1/network/slots/{slot_id}/cancel` Scope `network:write`. free. Cancel a scheduled slot Takes a slot off the grid by marking it cancelled, so the time is free again and the history stays readable. A slot that already aired cannot be cancelled. Cancelling a cancelled slot changes nothing. ```bash curl 'https://boldstudios.io/api/v1/network/slots/{slot_id}/cancel' -H "Authorization: Bearer $BOLD_API_KEY" ``` Returns: ```json { "slot_id": "e5b7c9d1-6f80-41a2-84d5-5f7a9b1c3d65", "status": "cancelled", "kind": "vod", "title": "Kingdom Stories" } ``` #### network.asrun `GET https://boldstudios.io/api/v1/network/networks/{network_id}/asrun` Scope `network:read`. free. The as-run log: what actually aired Every program, live segment and commercial that aired, newest first, with start, end, length, source (playout, control room, manual or api), and for commercials the house number and advertiser. Defaults to the last 24 hours; at most 93 days and 1000 entries. ```bash curl 'https://boldstudios.io/api/v1/network/networks/{network_id}/asrun?from=2026-10-04T00:00:00Z&limit=2' -H "Authorization: Bearer $BOLD_API_KEY" ``` Returns: ```json { "from": "2026-10-04T00:00:00Z", "to": "2026-10-04T18:00:00Z", "entries": [ { "asrun_id": 88214, "channel_id": "2c7e9a14-5b3d-4e8f-a061-7d2b9c4e1f30", "kind": "ad", "title": "Gold Co :30", "house_number": "GLD-0930", "advertiser": "Gold Co", "started_at": "2026-10-04T14:00:05Z", "ended_at": "2026-10-04T14:00:35Z", "duration_sec": 30, "source": "playout" }, { "asrun_id": 88213, "channel_id": "2c7e9a14-5b3d-4e8f-a061-7d2b9c4e1f30", "kind": "live", "title": "Take Five", "started_at": "2026-10-04T13:00:00Z", "ended_at": "2026-10-04T14:00:02Z", "duration_sec": 3602, "source": "playout" } ] } ``` #### network.log_asrun `POST https://boldstudios.io/api/v1/network/channels/{channel_id}/asrun` Scope `network:write`. free. Log what aired from your own playout Records an airing from a playout system you run yourself, so the as-run log, ad delivery and audience reports cover it. Send external_ref (your id for the airing) with the start, then again with ended_at: both land on one entry, and a retry changes nothing. A spot_id marks the airing as a commercial and counts toward its order's delivery. ```bash curl -X POST 'https://boldstudios.io/api/v1/network/channels/{channel_id}/asrun' \ -H "Authorization: Bearer $BOLD_API_KEY" \ -H 'Content-Type: application/json' \ -d '{"title":"Morning Prayer","kind":"vod","started_at":"2026-10-04T10:00:00Z","ended_at":"2026-10-04T10:29:40Z","external_ref":"playout-7731"}' ``` Returns: ```json { "asrun_id": 88301, "channel_id": "2c7e9a14-5b3d-4e8f-a061-7d2b9c4e1f30", "kind": "vod", "title": "Morning Prayer", "started_at": "2026-10-04T10:00:00Z", "ended_at": "2026-10-04T10:29:40Z", "duration_sec": 1780, "source": "api", "external_ref": "playout-7731" } ``` #### network.audience `GET https://boldstudios.io/api/v1/network/networks/{network_id}/audience` Scope `network:read`. free. Audience by channel, platform, country and program Concurrent viewers over a period (default the last 7 days, at most 93): totals with peak and viewer minutes, a series by minute, hour or day per channel and platform, the top countries, and each program's airings, average and peak audience. ```bash curl 'https://boldstudios.io/api/v1/network/networks/{network_id}/audience?from=2026-10-01T00:00:00Z&bucket=day' -H "Authorization: Bearer $BOLD_API_KEY" ``` Returns: ```json { "from": "2026-10-01T00:00:00Z", "to": "2026-10-04T18:00:00Z", "bucket": "day", "totals": { "viewer_minutes": 418220, "avg_viewers": 104.6, "peak_viewers": 1880, "peak_at": "2026-10-03T23:14:00Z" }, "series": [ { "bucket": "2026-10-03T00:00:00Z", "channel_id": "2c7e9a14-5b3d-4e8f-a061-7d2b9c4e1f30", "platform": "roku", "avg_viewers": 61.2, "peak_viewers": 940, "viewer_minutes": 88130 } ], "platforms": [ { "platform": "web", "viewer_minutes": 201400 }, { "platform": "roku", "viewer_minutes": 154100 } ], "countries": [ { "country": "US", "viewer_minutes": 301200 }, { "country": "NG", "viewer_minutes": 40300 } ], "programs": [ { "title": "Take Five", "kind": "live", "airings": 4, "minutes_aired": 240, "avg_viewers": 412.5, "peak_viewers": 1880 } ] } ``` #### network.list_bookings `GET https://boldstudios.io/api/v1/network/networks/{network_id}/bookings` Scope `network:read`. free. Guest bookings Guests booked across the network's shows, by air time: status (prospect, invited, confirmed, prepped, aired, declined, cancelled), topic, show, rundown segment and the guest's name, organisation, title and photo. Contact details are never returned. ```bash curl 'https://boldstudios.io/api/v1/network/networks/{network_id}/bookings?status=confirmed' -H "Authorization: Bearer $BOLD_API_KEY" ``` Returns: ```json { "from": "2026-10-03T18:00:00Z", "to": "2026-12-02T18:00:00Z", "bookings": [ { "booking_id": "f6c8d0e2-7a91-42b3-95e6-6a8b0c2d4e76", "status": "confirmed", "slot_at": "2026-10-06T13:20:00Z", "duration_sec": 600, "topic": "Faith at work", "remote": true, "show_title": "Take Five", "rundown_item_id": "07d9e1f3-8b02-43c4-a6f7-7b9c1d3e5f87", "guest": { "guest_id": "18e0f2a4-9c13-44d5-b708-8c0d2e4f6a98", "name": "Dana Lee", "organization": "Hope Inc", "title": "CEO" } } ] } ``` #### network.book_guest `POST https://boldstudios.io/api/v1/network/networks/{network_id}/bookings` Scope `network:write`. free. Book a guest Books a guest for a show, optionally onto a rundown segment, which fills that segment's guest name and lower third once the booking is confirmed. Name an existing guest_id, or give guest details and the guest is added (or matched to the same person already on file). Nothing is sent to the guest. ```bash curl -X POST 'https://boldstudios.io/api/v1/network/networks/{network_id}/bookings' \ -H "Authorization: Bearer $BOLD_API_KEY" \ -H 'Content-Type: application/json' \ -d '{"guest":{"name":"Dana Lee","organization":"Hope Inc","title":"CEO","email":"dana@example.com"},"show_id":"d4a6b8c0-5e7f-4091-b3c4-4e6f8a0b2c54","rundown_item_id":"07d9e1f3-8b02-43c4-a6f7-7b9c1d3e5f87","slot_at":"2026-10-06T13:20:00Z","duration_sec":600,"topic":"Faith at work","status":"confirmed"}' ``` Returns: ```json { "booking_id": "f6c8d0e2-7a91-42b3-95e6-6a8b0c2d4e76", "status": "confirmed", "slot_at": "2026-10-06T13:20:00Z", "topic": "Faith at work", "show_title": "Take Five", "rundown_item_id": "07d9e1f3-8b02-43c4-a6f7-7b9c1d3e5f87", "guest": { "guest_id": "18e0f2a4-9c13-44d5-b708-8c0d2e4f6a98", "name": "Dana Lee", "organization": "Hope Inc", "title": "CEO" } } ``` #### network.update_booking `POST https://boldstudios.io/api/v1/network/bookings/{booking_id}` Scope `network:write`. free. Update a guest booking Moves a booking along (invited, confirmed, prepped, aired, declined, cancelled), or changes its time, length, topic, notes or rundown segment. Fields left out stay as they are, so repeating the call changes nothing further. ```bash curl -X POST 'https://boldstudios.io/api/v1/network/bookings/{booking_id}' \ -H "Authorization: Bearer $BOLD_API_KEY" \ -H 'Content-Type: application/json' \ -d '{"status":"aired"}' ``` Returns: ```json { "booking_id": "f6c8d0e2-7a91-42b3-95e6-6a8b0c2d4e76", "status": "aired", "slot_at": "2026-10-06T13:20:00Z", "guest": { "guest_id": "18e0f2a4-9c13-44d5-b708-8c0d2e4f6a98", "name": "Dana Lee" } } ``` #### network.rundown `GET https://boldstudios.io/api/v1/network/rundowns/{rundown_id}` Scope `network:read`. free. Read a rundown One show's rundown in order: each segment's kind, title, planned and aired length, talent, guest, lower third, layout, status and script, and which segment is live now. ```bash curl 'https://boldstudios.io/api/v1/network/rundowns/{rundown_id}' -H "Authorization: Bearer $BOLD_API_KEY" ``` Returns: ```json { "rundown_id": "29f1a3b5-0d24-45e6-c819-9d1e3f5a7b09", "show_title": "Take Five", "title": "Monday 6 October", "air_at": "2026-10-06T13:00:00Z", "planned_duration_sec": 3600, "mode": "live", "status": "ready", "live_item_id": null, "items": [ { "item_id": "3a02b4c6-1e35-46f7-d92a-0e2f4a6b8c1a", "kind": "open", "title": "Cold open", "duration_sec": 90, "status": "pending", "script": "Good morning and welcome to Take Five." }, { "item_id": "07d9e1f3-8b02-43c4-a6f7-7b9c1d3e5f87", "kind": "interview", "title": "Faith at work", "duration_sec": 600, "guest_name": "Dana Lee", "lower_third_name": "Dana Lee", "lower_third_sub": "CEO, Hope Inc", "layout": "split", "status": "pending" } ] } ``` ### BOLD Stream Live video for your own platform: create a live event, send each guest their own join link, simulcast to YouTube, Facebook, X, Rumble and any RTMP at once, and play it on your site with one embeddable player that becomes the replay when the show ends. Billed per minute while a show runs. #### stream.create_event `POST https://boldstudios.io/api/v1/stream/events` Scope `stream:write`. Free to create. While live: 40 credits ($0.04) per minute per live output, 4 credits ($0.004) per person per minute, 40 credits ($0.04) per cloud recording minute; 4K recording adds 50 credits ($0.05) per minute. 720p and 1080p recording are included. Create a live event Creates a BOLD Stream live event and its studio. Returns the embeddable player_url for your site (live while on air, the replay after), the studio_url for your producer, a guest_join_url plus one named link per guest, and whether it is listed on BOLD Live. Send external_ref (your own id for the thing being streamed) and a repeat call returns the same event instead of a second one. Send brand_id to own it as a client brand you produce for. ```bash curl -X POST 'https://boldstudios.io/api/v1/stream/events' \ -H "Authorization: Bearer $BOLD_API_KEY" \ -H 'Content-Type: application/json' \ -d '{"title":"Massachusetts U.S. Senate Debate","scheduled_at":"2026-10-29T23:00:00Z","external_ref":"debate:ma-us-senate-2026","listed":false,"guests":["Candidate A","Candidate B","Moderator"]}' ``` Returns: ```json { "created": true, "event": { "event_id": "5a0c1b7e-6f0e-4c55-9a52-2d9f6a1f0c11", "phase": "upcoming", "on_air": false, "player_url": "https://boldstudios.io/stream/play/5a0c1b7e-6f0e-4c55-9a52-2d9f6a1f0c11", "studio_url": "https://boldstudios.io/stream/s-9b2f0c64-1d8e-4a8f-b1c7-6e0c2a4f7d10", "guest_links": [ { "name": "Candidate A", "url": "https://boldstudios.io/stream/j-k7mq2xr9ta?name=Candidate%20A" } ], "listed_on_bold_live": false, "destinations": [] } } ``` #### stream.list_events `GET https://boldstudios.io/api/v1/stream/events` Scope `stream:read`. free. Your live events The 50 most recent live events you own, or every event of a client brand when brand_id is given, each with its phase (upcoming, live, replay or ended), viewers and links. No worked example yet. `GET https://boldstudios.io/api/v1` carries the current shape. #### stream.get_event `GET https://boldstudios.io/api/v1/stream/events/{event_id}` Scope `stream:read`. free. One live event Phase, whether video is really on air, live viewers and peak, the player, studio and guest links, the HLS addresses, the BOLD Live listing and every simulcast destination with whether it is on air now. No worked example yet. `GET https://boldstudios.io/api/v1` carries the current shape. #### stream.update_event `POST https://boldstudios.io/api/v1/stream/events/{event_id}` Scope `stream:write`. free. Change a live event Change the title, description, start time, cover, guest names or BOLD Live listing, or cancel it with cancelled true. Only the fields sent are changed. No worked example yet. `GET https://boldstudios.io/api/v1` carries the current shape. #### stream.end_event `POST https://boldstudios.io/api/v1/stream/events/{event_id}/end` Scope `stream:write`. free. End a live event Takes the show off air everywhere, joins every part into one replay and marks the event ended. The same player_url plays the replay from then on. Going live again from the studio resumes the same event. No worked example yet. `GET https://boldstudios.io/api/v1` carries the current shape. #### stream.list_destinations `GET https://boldstudios.io/api/v1/stream/destinations` Scope `stream:read`. free. Simulcast destinations Your saved simulcast destinations (YouTube, Facebook, X, Rumble, Twitch, Kick, LinkedIn, TikTok, Instagram or any RTMP, RTMPS or SRT), or a client brand's with brand_id. Stream keys are never returned, only their last four characters. The producer turns each one on in the studio's Go live tab. No worked example yet. `GET https://boldstudios.io/api/v1` carries the current shape. #### stream.save_destination `POST https://boldstudios.io/api/v1/stream/destinations` Scope `stream:write`. free. Add a simulcast destination Saves a platform and its stream key, encrypted in the vault and never shown again. rtmp_url defaults to the platform's own for YouTube, Facebook and Twitch. Portrait (9:16) is the default for TikTok and Instagram, landscape (16:9) for the rest. No worked example yet. `GET https://boldstudios.io/api/v1` carries the current shape. #### stream.delete_destination `DELETE https://boldstudios.io/api/v1/stream/destinations/{destination_id}` Scope `stream:write`. free. Remove a simulcast destination Deletes the destination and its stored key. Send brand_id in the query for a client brand's destination. No worked example yet. `GET https://boldstudios.io/api/v1` carries the current shape. #### stream.feature_comment `POST https://boldstudios.io/api/v1/stream/events/{event_id}/featured-comment` Scope `stream:write`. free. Put a comment on screen Shows a comment as a card on the live picture: in the studio, on the event player and on every simulcast destination (YouTube, Facebook, X). It stays up for seconds (10 by default) or, with pin true, until it is cleared or replaced. Send chat_id to feature a message already in the event's chat, or name and text for any comment. No worked example yet. `GET https://boldstudios.io/api/v1` carries the current shape. #### stream.clear_comment `POST https://boldstudios.io/api/v1/stream/events/{event_id}/featured-comment/clear` Scope `stream:write`. free. Take the comment off screen Removes the featured comment from the live picture at once. No worked example yet. `GET https://boldstudios.io/api/v1` carries the current shape. #### stream.post_chat `POST https://boldstudios.io/api/v1/stream/events/{event_id}/chat` Scope `stream:write`. free. Post a viewer comment Adds a viewer's comment to the event's chat, where the producer sees it next to the room chat and BOLD Social and platform comments, and can put it on screen. Send your own message id as external_id so a retry never posts twice. No worked example yet. `GET https://boldstudios.io/api/v1` carries the current shape. #### stream.list_chat `GET https://boldstudios.io/api/v1/stream/events/{event_id}/chat` Scope `stream:read`. free. Read the event chat The latest 200 viewer and platform comments on the event, oldest first, with since to fetch only newer ones, and the comment on screen now (featured) so your page can draw it too. No worked example yet. `GET https://boldstudios.io/api/v1` carries the current shape. #### stream.set_rundown `POST https://boldstudios.io/api/v1/stream/events/{event_id}/rundown` Scope `stream:write`. free. Set the run of show The show's segments (title, subtitle, start time, minutes, people) and the calls to action that rotate with them on the lower third. The producer advances segments in the studio, or they advance on schedule with auto_advance. Each segment that goes on air becomes a chapter of the replay. No worked example yet. `GET https://boldstudios.io/api/v1` carries the current shape. #### stream.captions_token `POST https://boldstudios.io/api/v1/stream/events/{event_id}/captions-token` Scope `stream:write`. free. Live captions token A short-lived token (60 seconds to connect, sessions up to 10 minutes) for AssemblyAI real-time captions of one speaker of your event, plus the websocket address and audio format to send (16 kHz, 16-bit mono PCM, about 50 ms a frame). The API key never leaves BOLD. Each token is billed as a 10 minute block of live captions to the event owner in BOLD credits; one block per speaker per 10 minutes however often you ask. No worked example yet. `GET https://boldstudios.io/api/v1` carries the current shape. #### stream.get_summary `GET https://boldstudios.io/api/v1/stream/events/{event_id}/summary` Scope `stream:read`. free. AI summary and show notes After the event ends: a summary, decisions, action items with owners, chapters with timestamps, show notes, a YouTube description with chapters, social post drafts and an email draft (never sent). status is running, ready, failed or skipped; null until the event has ended and its transcript is in. No worked example yet. `GET https://boldstudios.io/api/v1` carries the current shape. #### stream.export `POST https://boldstudios.io/api/v1/stream/events/{event_id}/export` Scope `stream:write`. free. Export the edited recording Renders the event's recording with every cut made in BOLD Cut, from the full-quality originals. studio_sound runs noise reduction, de-reverb and leveling on each voice before the mix; loudness_lufs sets the target (-16 for podcasts, the default, -14 for video platforms); captions burns in captions. Billed per minute of finished video when the render lands. Returns the job; poll with the same call's status field via stream.get_event. No worked example yet. `GET https://boldstudios.io/api/v1` carries the current shape. #### stream.create_poll `POST https://boldstudios.io/api/v1/stream/events/{event_id}/polls` Scope `stream:write`. free. Create a poll for a live event Creates a poll on your live event with 2 to 6 options. Send open true to open it at once: viewers on the BOLD player, every site that embeds it and people in the studio can vote, and the producer can put the live result on screen with one tap. No worked example yet. `GET https://boldstudios.io/api/v1` carries the current shape. #### stream.vote `POST https://boldstudios.io/api/v1/stream/events/{event_id}/polls/{poll_id}/vote` Scope `stream:write`. free. Vote in a live poll for one of your users Records one vote in an open poll for one of your own users, named by voter_ref (your id for them, never shown). One vote per voter_ref per poll: voting again changes it. Returns the live tally. No worked example yet. `GET https://boldstudios.io/api/v1` carries the current shape. #### stream.list_questions `GET https://boldstudios.io/api/v1/stream/events/{event_id}/questions` Scope `stream:read`. free. Audience questions for a live event The audience questions on your live event, most upvoted first, with each one's status (open, answered or hidden). Add status to filter. No worked example yet. `GET https://boldstudios.io/api/v1` carries the current shape. #### stream.ask `POST https://boldstudios.io/api/v1/stream/events/{event_id}/questions` Scope `stream:write`. free. Ask a question on a live event for one of your users Adds an audience question for one of your own users (voter_ref is your id for them, never shown; name is what the host sees). The producer sees it in the Q and A queue and can put it on screen. No worked example yet. `GET https://boldstudios.io/api/v1` carries the current shape. #### stream.show_report `GET https://boldstudios.io/api/v1/stream/events/{event_id}/report` Scope `stream:read`. free. Post-show report for a live event What the show did, per platform: peak and average concurrent viewers, watch minutes, chat messages, polls and questions, the minutes with the most chat, and a short list of next actions. No worked example yet. `GET https://boldstudios.io/api/v1` carries the current shape. ### BOLD Talk Team, client and one-to-one messaging for your own platform: workspaces, channels, private groups and direct messages, with live delivery, reactions and read state. Any conversation exports as a clean Markdown transcript you can hand to an AI or a developer. #### talk.list_conversations `GET https://boldstudios.io/api/v1/talk/conversations` Scope `talk:read`. free. Your workspaces and conversations Every workspace you belong to and every conversation you are in (channels, private groups and direct messages), newest activity first, each with its unread count and a preview of the last message. No worked example yet. `GET https://boldstudios.io/api/v1` carries the current shape. #### talk.list_messages `GET https://boldstudios.io/api/v1/talk/conversations/{conversation_id}/messages` Scope `talk:read`. free. Messages in a conversation Up to 200 messages, oldest first, with reactions. Page backwards with before (the created_at of the oldest message you have). Deleted messages come back marked deleted with no content. No worked example yet. `GET https://boldstudios.io/api/v1` carries the current shape. #### talk.send_message `POST https://boldstudios.io/api/v1/talk/conversations/{conversation_id}/messages` Scope `talk:write`. free. Send a message Posts a text message as you. Send client_ref (your own id for the message) and a retry returns the first message instead of posting twice. Delivered live to everyone in the conversation. ```bash curl -X POST 'https://boldstudios.io/api/v1/talk/conversations/{conversation_id}/messages' \ -H "Authorization: Bearer $BOLD_API_KEY" \ -H 'Content-Type: application/json' \ -d '{"body":"The checkout button should say Give, not Buy. Can we ship that today?","client_ref":"msg-2041"}' ``` Returns: ```json { "message_id": "7c1e9f40-2b6d-4e8a-a1f3-0d9b5c6e7a12" } ``` #### talk.export_transcript `GET https://boldstudios.io/api/v1/talk/conversations/{conversation_id}/transcript` Scope `talk:read`. free. Export a transcript as Markdown The conversation as a Markdown document: who was there, every message with its author and time, voice notes as their transcripts, and a closing brief that asks a builder to list each agreed change. Optional from and to narrow it to a time range. ```bash curl 'https://boldstudios.io/api/v1/talk/conversations/{conversation_id}/transcriptfrom=2026-10-14T00:00:00Z' -H "Authorization: Bearer $BOLD_API_KEY" ``` Returns: ```json { "title": "Galileyo #requests", "filename": "talk-requests-2026-10-14.md", "messages": 2, "markdown": "# Galileyo / #requests\n\n_BOLD Talk transcript, 2 messages_\n\n## Conversation\n\n**Brett Miller** 2026-10-14 15:02 UTC\nThe checkout button should say Give, not Buy.\n\n**Brett Raio** 2026-10-14 15:04 UTC\nAgreed, shipping it today.\n" } ``` #### talk.create_workspace `POST https://boldstudios.io/api/v1/talk/workspaces` Scope `talk:write`. free. Create a workspace Creates a team workspace with you as its owner and a #general channel. No worked example yet. `GET https://boldstudios.io/api/v1` carries the current shape. #### talk.add_member `POST https://boldstudios.io/api/v1/talk/workspaces/{workspace_id}/members` Scope `talk:write`. free. Add someone to a workspace Adds a person who already has a BOLD account, by email, as admin, member or guest. Members join every public channel; guests only join what they are invited to. You must be an owner or admin of the workspace. No worked example yet. `GET https://boldstudios.io/api/v1` carries the current shape. #### talk.create_channel `POST https://boldstudios.io/api/v1/talk/workspaces/{workspace_id}/channels` Scope `talk:write`. free. Create a channel or group A public channel (every member joins), a private channel or a group conversation. For private channels and groups, members lists the people to add; only people already in the workspace can be added. No worked example yet. `GET https://boldstudios.io/api/v1` carries the current shape. #### talk.open_dm `POST https://boldstudios.io/api/v1/talk/dms` Scope `talk:write`. free. Open a direct message Opens (or returns the existing) one-to-one conversation with someone you share a workspace with. No worked example yet. `GET https://boldstudios.io/api/v1` carries the current shape. #### talk.get_settings `GET https://boldstudios.io/api/v1/talk/conversations/{conversation_id}/settings` Scope `talk:read`. free. A conversation's settings Name, topic, description, icon, colour, welcome note, who can post, who can add people, whether files, voice notes, reactions and calls are allowed, slow mode, archive state, how many people and pins it has, and what you may do there (your role and each permission). No worked example yet. `GET https://boldstudios.io/api/v1` carries the current shape. #### talk.update_settings `PATCH https://boldstudios.io/api/v1/talk/conversations/{conversation_id}/settings` Scope `talk:write`. free. Change a conversation's settings Changes only the fields sent. Conversation-wide settings need the owner or an admin of that conversation (or of its workspace); pinned and muted_until are your own and anyone in the conversation can set them. Returns the settings as they now are. No worked example yet. `GET https://boldstudios.io/api/v1` carries the current shape. #### talk.list_members `GET https://boldstudios.io/api/v1/talk/conversations/{conversation_id}/members` Scope `talk:read`. free. People in a conversation Everyone in the conversation with their role there (owner, admin, moderator, member or read only), their workspace role and side, and when they last read it. No worked example yet. `GET https://boldstudios.io/api/v1` carries the current shape. #### talk.add_people `POST https://boldstudios.io/api/v1/talk/conversations/{conversation_id}/members` Scope `talk:write`. free. Add people to a conversation Adds people who are already in the conversation's workspace. Whether you may follows the conversation's add setting. Returns how many were added; someone already there is not counted. No worked example yet. `GET https://boldstudios.io/api/v1` carries the current shape. #### talk.remove_person `DELETE https://boldstudios.io/api/v1/talk/conversations/{conversation_id}/members/{user_id}` Scope `talk:write`. free. Remove someone from a conversation Removes a person (or yourself, to leave). Removing someone else needs the owner or an admin. If the owner leaves, the longest-standing admin takes over. No worked example yet. `GET https://boldstudios.io/api/v1` carries the current shape. #### talk.set_role `PUT https://boldstudios.io/api/v1/talk/conversations/{conversation_id}/members/{user_id}/role` Scope `talk:write`. free. Set someone's role in a conversation owner, admin, moderator, member or viewer (read only). Needs the owner or an admin; only the owner (or a workspace admin) can hand over ownership, and the old owner stays on as an admin. No worked example yet. `GET https://boldstudios.io/api/v1` carries the current shape. #### talk.list_pins `GET https://boldstudios.io/api/v1/talk/conversations/{conversation_id}/pins` Scope `talk:read`. free. Pinned messages The conversation's pinned messages, newest pin first, with who wrote and who pinned each. No worked example yet. `GET https://boldstudios.io/api/v1` carries the current shape. #### talk.pin_message `PUT https://boldstudios.io/api/v1/talk/messages/{message_id}/pin` Scope `talk:write`. free. Pin or unpin a message pinned true pins it to the top of its conversation, false unpins it. In channels this needs a moderator, admin or owner; in groups and direct messages anyone in it can pin. Up to 50 pins per conversation. No worked example yet. `GET https://boldstudios.io/api/v1` carries the current shape. #### talk.browse_channels `GET https://boldstudios.io/api/v1/talk/workspaces/{workspace_id}/channels` Scope `talk:read`. free. Channels in a workspace Every channel in the workspace you can see, joined or not, with member counts. Private channels show only to their members and the workspace's admins. No worked example yet. `GET https://boldstudios.io/api/v1` carries the current shape. #### talk.join_channel `POST https://boldstudios.io/api/v1/talk/conversations/{conversation_id}/join` Scope `talk:write`. free. Join a channel Joins a public channel in a workspace you belong to (not as a guest). Workspace admins can also step into private channels. Joining one you are already in does nothing. No worked example yet. `GET https://boldstudios.io/api/v1` carries the current shape. ### BOLD Artist An artist's whole catalog as data: every release with its tracks, ISRCs and UPCs, credits and agreed splits, release and listen links, and which royalty streams are being collected. One source for BOLD Music, His Glory Music, partner networks and the artist's own website. #### artist.list_releases `GET https://boldstudios.io/api/v1/artist/releases` Scope `artist:read`. free. Your releases Every release on your account, newest first: title, artists, type, release date (the original date for back catalog), UPC, label, status, whether it is in the stores, track count and its release link. ```bash curl 'https://boldstudios.io/api/v1/artist/releases?limit=2' -H "Authorization: Bearer $BOLD_API_KEY" ``` Returns: ```json { "releases": [ { "release_id": "f4d58576-6f8d-48e9-b552-99873e6f4216", "title": "Holy Faith", "primary_artist": "DJ Evon, Kieran the Light & Isaiah Robin", "type": "single", "release_date": "2023-04-28", "upc": "197342122603", "label": "BOLD United", "status": "approved", "in_stores": true, "tracks": 1, "link": "https://boldsocial.io/holy-faith" } ] } ``` #### artist.get_release `GET https://boldstudios.io/api/v1/artist/releases/{release_id}` Scope `artist:read`. free. One release in full A release with its tracks (ISRC, ISWC, duration), credits, agreed splits (names, roles, shares and whether each person has signed, never their emails) and its release links with every live store link. No worked example yet. `GET https://boldstudios.io/api/v1` carries the current shape. #### artist.list_links `GET https://boldstudios.io/api/v1/artist/links` Scope `artist:read`. free. Your release links Every live pre-save and listen link with its scans and pre-saves. No worked example yet. `GET https://boldstudios.io/api/v1` carries the current shape. #### artist.royalty_coverage `GET https://boldstudios.io/api/v1/artist/coverage` Scope `artist:read`. free. Which royalties you are collecting Each royalty stream a song can earn (stores, SoundExchange, Content ID, neighbouring rights, PRO performance, MLC mechanicals, sync, lyrics), whether yours is covered, and what to do about any that is not. No worked example yet. `GET https://boldstudios.io/api/v1` carries the current shape. ### BOLD Royalties Royalty accounting for labels and artists: import any distributor's statement, set and lock who owns what share of each release or track, record payouts, and read exactly who is owed what, live. #### royalties.balances `GET https://boldstudios.io/api/v1/royalties/balances` Scope `royalties:read`. free. Who is owed what Every payee on your ledger with what they earned under their shares, what they have been paid and what is owed, plus totals for money received, money not yet matched to a release and money on releases whose shares are not set. ```bash curl 'https://boldstudios.io/api/v1/royalties/balances' -H "Authorization: Bearer $BOLD_API_KEY" ``` Returns: ```json { "payees": [ { "payee_id": "6a1d1c52-2b5e-4c1a-9d0e-2f7b3c8e9a10", "name": "Kieran the Light", "has_account": false, "earned_cents": 2625, "paid_cents": 0, "owed_cents": 2625, "earned_by_source": { "empire": 26.25 }, "provisional": false, "last_paid_on": null, "payout_method": "cashapp" } ], "totals": { "statement_lines": 2, "received_usd": 92, "unmatched_usd": 0, "pool_usd": 80.5, "unallocated_pool_usd": 0, "earned_cents": 8050, "paid_cents": 0, "owed_cents": 8050, "overpaid_cents": 0 }, "periods": { "first": "2025-07-01", "last": "2025-07-01" } } ``` #### royalties.list_releases `GET https://boldstudios.io/api/v1/royalties/releases` Scope `royalties:read`. free. Money and shares per release Per release: statement lines, money received, the artist pool under the terms that apply, and whether its shares are locked, proposed, missing or do not total 100. No worked example yet. `GET https://boldstudios.io/api/v1` carries the current shape. #### royalties.import_statement `POST https://boldstudios.io/api/v1/royalties/statements` Scope `royalties:write`. free. Import a distributor statement Send the rows of a statement export (EMPIRE, Too Lost or any other) as objects. Column names are recognised in the forms distributors use (Sales Month, ISRC, UPC, Track Title, Store, Net Amount and their variants). Lines match to releases by ISRC, then UPC, then an unambiguous title. Re-sending a statement adds nothing, and two identical lines in one statement are both kept. The source must have terms set on the ledger. ```bash curl -X POST 'https://boldstudios.io/api/v1/royalties/statements' \ -H "Authorization: Bearer $BOLD_API_KEY" \ -H 'Content-Type: application/json' \ -d '{"source":"empire","filename":"empire-2025-07.csv","rows":[{"Sales Month":"2025-07","ISRC":"USA2P2312345","Track Title":"Holy Faith","Store":"Spotify","Net Amount":"100.00"},{"Sales Month":"2025-07","UPC":"197342375320","Track Title":"Wake Up America","Store":"Apple Music","Net Amount":"(8.00)"}]}' ``` Returns: ```json { "batch_id": "0b6f6a0e-77a4-4c43-9d43-1d2a7f0b4e21", "added": 2, "duplicates": 0, "rejected": 0, "amount_usd": 92, "match": { "by_isrc": 1, "by_upc": 1, "by_title": 0, "unmatched": 0 }, "rejected_examples": [] } ``` #### royalties.set_shares `POST https://boldstudios.io/api/v1/royalties/releases/{release_id}/shares` Scope `royalties:write`. free. Set who owns what Replaces the share set of a release, or of one track by ISRC. Shares must total exactly 100, each with at most 4 decimal places. A locked set is only replaced when unlock: true is sent. Name a payee by payee_id, or by name to add them. Send lock: true to confirm it. ```bash curl -X POST 'https://boldstudios.io/api/v1/royalties/releases/{release_id}/shares' \ -H "Authorization: Bearer $BOLD_API_KEY" \ -H 'Content-Type: application/json' \ -d '{"lock":true,"shares":[{"name":"DJ Evon","role":"producer","share_pct":30},{"name":"Kieran the Light","role":"artist","share_pct":30},{"name":"Isaiah Robin","role":"artist","share_pct":30},{"name":"John Brown","role":"writer","share_pct":10}]}' ``` Returns: ```json { "release_id": "f4d58576-6f8d-48e9-b552-99873e6f4216", "track_isrc": null, "shares": 4, "locked": true } ``` #### royalties.record_payment `POST https://boldstudios.io/api/v1/royalties/payments` Scope `royalties:write`. free. Record a payout Records money paid to a payee so their balance stays true. A payment with the same payee, date and amount as one already recorded comes back as duplicate: true and is not recorded; send allow_duplicate: true if it really is a second payment. No worked example yet. `GET https://boldstudios.io/api/v1` carries the current shape. #### royalties.my_statement `GET https://boldstudios.io/api/v1/royalties/me` Scope `royalties:read`. free. What you are owed For a collaborator whose BOLD account is linked on a label's ledger: what you earned, were paid and are owed, per label. No worked example yet. `GET https://boldstudios.io/api/v1` carries the current shape. ### BOLD Promote Run advertising across BOLD Social, Meta, Google, TikTok and your own platform from one API. Plan a budget, generate the creative, launch, and let the optimizer work it. #### promote.plan `POST https://boldstudios.io/api/v1/promote/plan` Scope `promote:read`. free. Plan a campaign before spending anything Give it a budget, an objective and a timeframe. It returns the channel allocation, projected impressions and results against real benchmarks, the management fee, and every channel that will refuse this campaign with the reason. Nothing is created. No worked example yet. `GET https://boldstudios.io/api/v1` carries the current shape. #### promote.create_campaign `POST https://boldstudios.io/api/v1/promote/campaigns` Scope `promote:write`. free. Create a campaign from a plan Creates the campaign and its per-channel slices in draft. Nothing reaches a platform until launch. No worked example yet. `GET https://boldstudios.io/api/v1` carries the current shape. #### promote.console `POST https://boldstudios.io/api/v1/promote/console` Scope `promote:read`. free. Read the whole account in one call The approval queue, connections, campaigns with their metrics, channel breakdown, creative leaderboard, compliance windows and fees. No worked example yet. `GET https://boldstudios.io/api/v1` carries the current shape. #### promote.report `POST https://boldstudios.io/api/v1/promote/report` Scope `promote:read`. free. Daily metrics across every channel One normalized shape whatever platform the numbers came from. Spend in cents, click-only conversions, and the video diagnostics that give you hook rate and hold rate. No worked example yet. `GET https://boldstudios.io/api/v1` carries the current shape. ### BOLD CRM Everybody your business knows, one record per person: search contacts, read a person's sources, notes and timeline, list segments and who is in them, find who has gone quiet, add notes and add or update a contact. Scoped to the CRM workspaces of the brands you belong to. #### crm.list_workspaces `GET https://boldstudios.io/api/v1/crm/workspaces` Scope `crm:read`. free. Your CRM workspaces Every CRM workspace you can see, one per brand you belong to, with your role, the number of people in it and its open deals. Use a workspace id with every other crm operation. ```bash curl 'https://boldstudios.io/api/v1/crm/workspaces' -H "Authorization: Bearer $BOLD_API_KEY" ``` Returns: ```json { "workspaces": [ { "id": "3d39e073-0031-46a6-9d74-f08ba22ae2ba", "name": "Open Hands Ministry", "slug": "open-hands-ministry", "role": "owner", "contacts": 66, "open_deals": 0, "is_internal": false } ] } ``` #### crm.search_contacts `GET https://boldstudios.io/api/v1/crm/workspaces/{workspace_id}/contacts` Scope `crm:read`. free. Search contacts People in a workspace, most recently active first, matched by name, email, phone or any text in the record. Narrow by lifecycle stage, tag or a saved segment. Each row lists where the person came from. ```bash curl 'https://boldstudios.io/api/v1/crm/workspaces/{workspace_id}/contacts?q=jordan&limit=1' -H "Authorization: Bearer $BOLD_API_KEY" ``` Returns: ```json { "total": 1, "limit": 1, "offset": 0, "rows": [ { "id": "2e828d67-5ace-4b74-900d-6356d8f6d8cd", "name": "Jordan Ellis", "email": "jordan@example.org", "phone": "(207) 555-0142", "lifecycle_stage": "lead", "tags": [ "subscribed", "form" ], "city": "Topsham", "last_activity_at": "2026-10-11T04:06:23Z", "sources": [ "BOLD Send audience", "Form submission" ] } ] } ``` #### crm.get_contact `GET https://boldstudios.io/api/v1/crm/workspaces/{workspace_id}/contacts/{contact_id}` Scope `crm:read`. free. A contact in full One person: their record, company, every email and phone on file, every source they came from, notes (pinned first), deals and the latest 200 timeline entries. No worked example yet. `GET https://boldstudios.io/api/v1` carries the current shape. #### crm.upsert_contact `POST https://boldstudios.io/api/v1/crm/workspaces/{workspace_id}/contacts` Scope `crm:write`. free. Add or update a contact Finds the person by email, then phone, or creates them, so one human is never two records. The fields you send replace what is stored, the way a hand edit does. Send contact_id to update a known person instead. Needs an owner, manager or editor role on the workspace. ```bash curl -X POST 'https://boldstudios.io/api/v1/crm/workspaces/{workspace_id}/contacts' \ -H "Authorization: Bearer $BOLD_API_KEY" \ -H 'Content-Type: application/json' \ -d '{"email":"jordan@example.org","name":"Jordan Ellis","lifecycle_stage":"engaged","tags":["volunteer"]}' ``` Returns: ```json { "contact_id": "2e828d67-5ace-4b74-900d-6356d8f6d8cd", "created": false } ``` #### crm.add_note `POST https://boldstudios.io/api/v1/crm/workspaces/{workspace_id}/contacts/{contact_id}/notes` Scope `crm:write`. free. Add a note to a contact Writes a note on a person as you, and puts it on their timeline. Needs an owner, manager or editor role on the workspace. ```bash curl -X POST 'https://boldstudios.io/api/v1/crm/workspaces/{workspace_id}/contacts/{contact_id}/notes' \ -H "Authorization: Bearer $BOLD_API_KEY" \ -H 'Content-Type: application/json' \ -d '{"body":"Called on Tuesday. Wants to help with the food drive in November."}' ``` Returns: ```json { "note_id": "69d2429e-59cf-4e3f-b582-53df480b4d5e" } ``` #### crm.list_segments `GET https://boldstudios.io/api/v1/crm/workspaces/{workspace_id}/segments` Scope `crm:read`. free. Saved segments Every saved segment in a workspace with its rules and the count from its last build. No worked example yet. `GET https://boldstudios.io/api/v1` carries the current shape. #### crm.segment_members `GET https://boldstudios.io/api/v1/crm/workspaces/{workspace_id}/segments/{segment_id}/contacts` Scope `crm:read`. free. Who is in a segment The people who match a segment's rules right now, counted live rather than from the last build, most recently active first. Optional q narrows it further. No worked example yet. `GET https://boldstudios.io/api/v1` carries the current shape. #### crm.followups `GET https://boldstudios.io/api/v1/crm/workspaces/{workspace_id}/followups` Scope `crm:read`. free. Who has gone quiet People with no activity in the last N days (30 unless you say), the most recently quiet first, with their last note and open deals. Leaves out anyone marked do not contact, archived or quarantined. ```bash curl 'https://boldstudios.io/api/v1/crm/workspaces/{workspace_id}/followups?days=14&limit=1' -H "Authorization: Bearer $BOLD_API_KEY" ``` Returns: ```json { "days": 14, "total": 23, "limit": 1, "offset": 0, "rows": [ { "id": "a6dbc281-5adc-4381-b689-7aefd582c2a8", "name": "Jackie Beal", "email": "jackie@example.org", "lifecycle_stage": "lead", "days_quiet": 15, "last_activity_at": "2026-09-30T21:15:31Z", "last_note_at": null, "open_deals": 0, "sources": [ "Form submission" ] } ] } ``` ### BOLD Campaign Read your BOLD Campaign campaigns: status, brief, budget planned against spent, channels, blockers, approvals and results so far. Read only; launches stay behind the human approval gate on BOLDStudios.io. #### campaign.list `GET https://boldstudios.io/api/v1/campaign/campaigns` Scope `campaign:read`. free. Your campaigns Every BOLD Campaign campaign you can see, latest anchor date first, with its brand, type, status, budget, objective and how many critical blockers it has. ```bash curl 'https://boldstudios.io/api/v1/campaign/campaigns' -H "Authorization: Bearer $BOLD_API_KEY" ``` Returns: ```json { "campaigns": [ { "campaign_id": "ba70bf0d-8dcb-432f-91f4-f925a5cfc4de", "key": "fall-outreach-2026", "name": "Fall Outreach 2026", "brand": "Open Hands Ministry", "type": "event", "anchor_date": "2026-11-08", "status": "planning", "budget_usd": 1500, "objective": "Fill the fall outreach with first-time guests from the neighbourhood.", "has_brief": true, "blockers": 0 } ] } ``` #### campaign.get `GET https://boldstudios.io/api/v1/campaign/campaigns/{campaign_id}` Scope `campaign:read`. free. A campaign and its results One campaign: status, brief, budget planned, committed and spent, each channel with its target, open blockers, approvals, and results so far (reach, ad, email, organic, press and lead totals with suggestions for the next run). ```bash curl 'https://boldstudios.io/api/v1/campaign/campaigns/{campaign_id}' -H "Authorization: Bearer $BOLD_API_KEY" ``` Returns: ```json { "campaign": { "campaign_id": "ba70bf0d-8dcb-432f-91f4-f925a5cfc4de", "name": "Fall Outreach 2026", "status": "planning", "anchor_date": "2026-11-08" }, "budget": { "planned": 1500, "committed": 0, "spent": 412.5, "remaining": 1087.5 }, "channels": [ { "channel": "meta", "status": "live", "bold_product": "BOLD Promote", "budget_usd": 1200, "target_metric": "registrations", "target_value": 150 } ], "blockers": [ { "blocker": "signoff_pending", "severity": "high", "detail": "Waiting on a sign-off for the second invitation email." } ], "approvals": [ { "scope": "send", "status": "pending", "channel": "email-2", "summary": "Second invitation email" } ], "results": { "range": { "from": "2026-10-01", "to": "2026-10-15", "timezone": "America/New_York" }, "totals": { "people_reached": 18240, "ad_clicks": 611, "spend_usd": 412.5, "emails_sent": 2210, "email_opens": 804 }, "suggestions": [ { "title": "Lead with the family photo ad", "detail": "It brought registrations at half the average cost.", "kind": "win", "channel": "ads" } ] } } ``` ### BOLD Clip Read your BOLD Clip projects, the footage in each, and every clip with its caption, status, render and the reason the moment was chosen. Read only. #### clip.list_projects `GET https://boldstudios.io/api/v1/clip/projects` Scope `clip:read`. free. Your clip projects Every BOLD Clip project you own, with posts per day, days of runway, whether it is under its floor, footage ready, moments ready and clips awaiting review. ```bash curl 'https://boldstudios.io/api/v1/clip/projects' -H "Authorization: Bearer $BOLD_API_KEY" ``` Returns: ```json { "projects": [ { "id": "f6c07525-76da-4ff3-8a87-ba663b6be13c", "name": "Open Hands Ministry", "posts_per_day": 3, "timezone": "America/New_York", "floor_days": 7, "runway_days": 6, "under_floor": true, "sources_ready": 4, "windows_ready": 12, "awaiting_review": 5 } ] } ``` #### clip.list_sources `GET https://boldstudios.io/api/v1/clip/projects/{project_id}/sources` Scope `clip:read`. free. Footage in a project The long-form videos and audio in a project, newest first, with status, length, how many times each has been used, whether it is transcribed and how many moments were found in it. No worked example yet. `GET https://boldstudios.io/api/v1` carries the current shape. #### clip.list_clips `GET https://boldstudios.io/api/v1/clip/projects/{project_id}/clips` Scope `clip:read`. free. Clips in a project Clips in a project, latest first, with caption, hashtags, status, schedule, where they post, the rendered video and the hook and scores of the moment they were cut from. Filter by status, for example needs_review. ```bash curl 'https://boldstudios.io/api/v1/clip/projects/{project_id}/clips?status=needs_review&limit=1' -H "Authorization: Bearer $BOLD_API_KEY" ``` Returns: ```json { "project_id": "f6c07525-76da-4ff3-8a87-ba663b6be13c", "total": 5, "limit": 1, "offset": 0, "rows": [ { "id": "4135a689-6be0-42d9-831b-00b359e68fc2", "status": "needs_review", "title": "He walked away from his only income for God", "caption": "He walked away from his only income. This is what happened next.", "lane": "testimony", "providers": [ "instagram", "tiktok", "youtube" ], "scheduled_at": null, "origin": "engine", "render": { "status": "rendered", "clip_url": "https://media.example/clip/4135a689.mp4", "thumb_url": "https://media.example/clip/4135a689.jpg" }, "moment": { "hook_text": "He walked away from his only income for God", "virality_score": 0.82, "retention_score": 0.74 } } ] } ``` #### clip.get_clip `GET https://boldstudios.io/api/v1/clip/clips/{clip_id}` Scope `clip:read`. free. A clip in full One clip: caption, status and schedule, the rendered video and its quality checks, and the moment it came from with its timing, quote, scores and the reason it was chosen. No worked example yet. `GET https://boldstudios.io/api/v1` carries the current shape. ### BOLD Studios Account, wallet and usage. #### studios.usage `GET https://boldstudios.io/api/v1/usage` Scope `studios:read`. free. Your calls and spend What this account has called and what it has spent, rolled up over the last days days: total calls, how many failed, spend in cents, and a per operation breakdown sorted by spend. days defaults to 30 and caps at 365. It is free, so it is the cheapest way to reconcile a bill or find the operation that is costing the most, but it reads at most the 5,000 most recent calls in the window, so a very busy account sees a shorter window than it asked for. ```bash curl 'https://boldstudios.io/api/v1/usage?days=30' -H "Authorization: Bearer $BOLD_API_KEY" ``` Returns: ```json { "period_days": 30, "calls": 412, "errors": 3, "spend_cents": 388, "spend": "$3.88", "by_operation": [ { "operation": "ai.chat", "calls": 380, "units": 388, "cents": 388, "errors": 3 } ] } ``` #### studios.balance `GET https://boldstudios.io/api/v1/balance` Scope `studios:read`. free. Your BOLD balance Your cleared BOLD wallet balance in cents and as a formatted string, plus whether auto reload is on and the threshold and top up amount it uses. This is the same number every paid call is authorized against, and the call is free, so it is the safe way to prove a key works before spending anything. Only cleared ledger entries count, so credit that is still settling does not appear here yet. ```bash curl 'https://boldstudios.io/api/v1/balance' -H "Authorization: Bearer $BOLD_API_KEY" ``` Returns: ```json { "balance_cents": 1911, "balance": "$19.11", "balance_credits": 19110, "currency": "usd", "plan_credits": { "available": 42000, "balance": "42,000 credits ($42.00)" }, "auto_reload": { "enabled": false }, "add_credit_url": "https://boldstudios.io/developers#credits" } ``` #### studios.health `GET https://boldstudios.io/api/v1/health` Scope `studios:read`. free. Your platforms' health Whether each of your platforms' databases is healthy, degraded or down right now, in plain words. Checked every minute. No worked example yet. `GET https://boldstudios.io/api/v1` carries the current shape. #### monitor.status `GET https://boldstudios.io/api/v1/monitor/status` Scope `monitor:admin`. free. Fleet health (BOLD admins) Every database BOLD runs, its live status and every open alert with the fix. Only for keys owned by a BOLD admin. No worked example yet. `GET https://boldstudios.io/api/v1` carries the current shape. ### BOLD Ops Briefs, blueprints, backlogs and the agents working them. Drive a build from your own code or your own agent. #### ops.list_builds `GET https://boldstudios.io/api/v1/ops/builds` Scope `ops:read`. free. Every build this key may see Returns one entry per build with its status, its playbook and how much of the backlog is finished. A build scoped to BOLD itself is never returned to an API key. ```bash curl 'https://boldstudios.io/api/v1/ops/builds' -H "Authorization: Bearer $BOLD_API_KEY" ``` Returns: ```json { "builds": [ { "brief_id": "1f2b8a5e-0c41-4f2a-9a6a-2b7f0a1d3e55", "title": "Ministry site rebuild", "status": "building", "scope": "brand", "source": "inbound", "target_url": "https://example.org", "playbook": "ministry-rebuild", "project": "Example Ministry", "tasks_total": 69, "tasks_done": 12, "updated_at": "2026-09-17T09:12:44Z" } ] } ``` #### ops.get_build `GET https://boldstudios.io/api/v1/ops/builds/{brief_id}` Scope `ops:read`. free. One build, with its current blueprint The brief, the newest blueprint including its markdown, and a count of tasks by status. Budget and spend are included only for a caller who can manage the build. ```bash curl 'https://boldstudios.io/api/v1/ops/builds/{brief_id}' -H "Authorization: Bearer $BOLD_API_KEY" ``` Returns: ```json { "brief_id": "1f2b8a5e-0c41-4f2a-9a6a-2b7f0a1d3e55", "title": "Ministry site rebuild", "status": "building", "playbook": "ministry-rebuild", "budget_credits": 1500000, "credits_spent": 43120, "blueprint": { "blueprint_id": "9c3d2f61-7c20-4f3e-9b0f-6a1c8e5d2a77", "version": 2, "title": "Ministry Rebuild", "status": "approved", "est_hours": 113.75, "approved_at": "2026-09-17T08:40:02Z" }, "task_counts": { "pending": 51, "running": 4, "verifying": 2, "verified": 12 } } ``` #### ops.list_tasks `GET https://boldstudios.io/api/v1/ops/builds/{brief_id}/tasks` Scope `ops:read`. free. The backlog Every task with its phase, priority, estimate, dependencies, assigned agent and how many verify rounds it has taken. ```bash curl 'https://boldstudios.io/api/v1/ops/builds/{brief_id}/tasks' -H "Authorization: Bearer $BOLD_API_KEY" ``` Returns: ```json { "tasks": [ { "ext_id": "T-020", "title": "Mapbox GL base map with clustering", "phase": "2 Map", "area": "Map", "priority": "P0", "status": "running", "rounds": 1, "owner": "BOLD", "agent": "builder", "est_hours": 3, "depends_on": [ "T-014" ], "product": null } ] } ``` #### ops.list_runs `GET https://boldstudios.io/api/v1/ops/builds/{brief_id}/runs` Scope `ops:read`. free. What the agents actually did Recent runs with status, attempt number and headline. Agent, model, credits and gate are returned only to a caller who can manage the build, because a client watching their own site being built does not see what it cost to produce. ```bash curl 'https://boldstudios.io/api/v1/ops/builds/{brief_id}/runs?limit=5' -H "Authorization: Bearer $BOLD_API_KEY" ``` Returns: ```json { "runs": [ { "run_id": "b7a1c0de-5f11-4a2b-8c39-1e6d4f2a9b03", "task": "T-020", "title": "Mapbox GL base map with clustering", "status": "succeeded", "kind": "build", "attempt": 2, "headline": "Clustered base map, dark style, expands on zoom", "finished_at": "2026-09-17T09:10:58Z", "duration_ms": 41200, "agent": "builder", "model": "claude-sonnet-5", "credits": 310, "gate": null } ] } ``` #### ops.ledger `GET https://boldstudios.io/api/v1/ops/builds/{brief_id}/ledger` Scope `ops:read`. free. What this build has cost Credits spent against budget, and tasks finished against total. Requires manage on the build. ```bash curl 'https://boldstudios.io/api/v1/ops/builds/{brief_id}/ledger' -H "Authorization: Bearer $BOLD_API_KEY" ``` Returns: ```json { "spent_credits": 43120, "budget_credits": 1500000, "tasks_done": 12, "tasks_total": 69 } ``` #### ops.list_playbooks `GET https://boldstudios.io/api/v1/ops/playbooks` Scope `ops:read`. free. The recipe library Every active playbook and how many tasks its template carries. Forking one into a brief gives you a blueprint and a full backlog in a single call. ```bash curl 'https://boldstudios.io/api/v1/ops/playbooks' -H "Authorization: Bearer $BOLD_API_KEY" ``` Returns: ```json { "playbooks": [ { "slug": "ministry-rebuild", "name": "Ministry Rebuild", "summary": "A ministry on a website builder with a real asset trapped inside it.", "tasks": 69 }, { "slug": "artist-site", "name": "Artist Site", "summary": "The template factory, about an hour each.", "tasks": 0 } ] } ``` #### ops.create_brief `POST https://boldstudios.io/api/v1/ops/builds` Scope `ops:write`. free. Open a build Creates a brief. Scope may be personal, or brand when the key owner owns or manages that brand. An API key can never open a build scoped to BOLD itself. ```bash curl -X POST 'https://boldstudios.io/api/v1/ops/builds' \ -H "Authorization: Bearer $BOLD_API_KEY" \ -H 'Content-Type: application/json' \ -d '{"title":"Ministry site rebuild","scope":"personal","target_url":"https://example.org","playbook_slug":"ministry-rebuild","raw_input":"They are on a website builder and the map is the asset."}' ``` Returns: ```json { "brief_id": "1f2b8a5e-0c41-4f2a-9a6a-2b7f0a1d3e55" } ``` #### ops.save_blueprint `POST https://boldstudios.io/api/v1/ops/builds/{brief_id}/blueprint` Scope `ops:write`. free. Write a new blueprint version Always creates a new version rather than editing in place, so a scope change is a diff between two rows instead of an argument about what was agreed. ```bash curl -X POST 'https://boldstudios.io/api/v1/ops/builds/{brief_id}/blueprint' \ -H "Authorization: Bearer $BOLD_API_KEY" \ -H 'Content-Type: application/json' \ -d '{"title":"Rebuild plan","markdown":"# Rebuild plan\n\nPhases, in order.","est_hours":113.75}' ``` Returns: ```json { "blueprint_id": "9c3d2f61-7c20-4f3e-9b0f-6a1c8e5d2a77", "version": 2 } ``` #### ops.import_tasks `POST https://boldstudios.io/api/v1/ops/blueprints/{blueprint_id}/tasks` Scope `ops:write`. free. Load or update the backlog Upserts tasks by their external id. Progress is never import data: a task that already exists keeps its status and its round count, so re-importing after a scope change cannot send finished work back to the start. ```bash curl -X POST 'https://boldstudios.io/api/v1/ops/blueprints/{blueprint_id}/tasks' \ -H "Authorization: Bearer $BOLD_API_KEY" \ -H 'Content-Type: application/json' \ -d '{"tasks":[{"ext_id":"T-001","title":"Import the existing data","priority":"P0","est_hours":3},{"ext_id":"T-002","title":"Check every row landed","depends_on":["T-001"],"est_hours":1}]}' ``` Returns: ```json { "ok": true, "rows": 2, "inserted": 2, "updated": 0 } ``` #### ops.approve_blueprint `POST https://boldstudios.io/api/v1/ops/blueprints/{blueprint_id}/approve` Scope `ops:write`. The agent's work is billed in plan credits by tokens used: 10 credits ($0.01) per 1k tokens on Haiku, 25 credits ($0.025) on Sonnet, 100 credits ($0.10) on Opus. Arm the backlog Approving is what makes tasks claimable. Until a blueprint is approved the runner has nothing to pick up. Merging code still requires a person regardless. ```bash curl 'https://boldstudios.io/api/v1/ops/blueprints/{blueprint_id}/approve' -H "Authorization: Bearer $BOLD_API_KEY" ``` Returns: ```json { "ok": true, "brief_id": "1f2b8a5e-0c41-4f2a-9a6a-2b7f0a1d3e55" } ``` #### ops.fork_playbook `POST https://boldstudios.io/api/v1/ops/builds/{brief_id}/playbook` Scope `ops:write`. free. Fork a playbook into a build Copies a playbook template in as a proposed blueprint plus its whole task backlog, dependencies included. ```bash curl -X POST 'https://boldstudios.io/api/v1/ops/builds/{brief_id}/playbook' \ -H "Authorization: Bearer $BOLD_API_KEY" \ -H 'Content-Type: application/json' \ -d '{"playbook":"ministry-rebuild"}' ``` Returns: ```json { "ok": true, "blueprint_id": "9c3d2f61-7c20-4f3e-9b0f-6a1c8e5d2a77", "tasks": { "ok": true, "rows": 69, "inserted": 69, "updated": 0 } } ``` #### ops.start_session `POST https://boldstudios.io/api/v1/ops/sessions` Scope `ops:write`. The agent's work is billed in plan credits by tokens used: 10 credits ($0.01) per 1k tokens on Haiku, 25 credits ($0.025) on Sonnet, 100 credits ($0.10) on Opus. Start an agent session Starts a BOLD Ops agent session as the key owner, exactly as if they had typed the prompt into BOLD Studios. Answers 201 with run_id and conversation_id; poll ops.get_session for progress and the answer. Send conversation_id to continue an earlier conversation: if it is still working the message is queued behind it and the answer is 202 with status queued. Free at the gateway: the session is charged in BOLD Studios credits per model turn (flash 10, pro 25, max 100 credits per 1,000 tokens), and a key whose owner has no credits gets 402 insufficient_credits. ```bash curl -X POST 'https://boldstudios.io/api/v1/ops/sessions' \ -H "Authorization: Bearer $BOLD_API_KEY" \ -H 'Content-Type: application/json' \ -d '{"prompt":"Audit https://example.com for broken links and write the fixes to fixes.md","tier":"pro","effort":"medium"}' ``` Returns: ```json { "status": "running", "run_id": "3c9e1f20-6a7b-4d8e-9f01-2b3c4d5e6f70", "conversation_id": "8a1b2c3d-4e5f-4a6b-8c7d-9e0f1a2b3c4d", "poll": "/v1/ops/sessions/3c9e1f20-6a7b-4d8e-9f01-2b3c4d5e6f70" } ``` #### ops.get_session `GET https://boldstudios.io/api/v1/ops/sessions/{run_id}` Scope `ops:read`. free. Read an agent session Status, whether it is finished, what it is waiting on you for (an answer or an approval), the final answer once finished, the task list, open questions, pending approvals, the files it wrote (name, id, size), credits spent and tokens. A session that is not yours reads as not found. ```bash curl 'https://boldstudios.io/api/v1/ops/sessions/{run_id}' -H "Authorization: Bearer $BOLD_API_KEY" ``` Returns: ```json { "run_id": "3c9e1f20-6a7b-4d8e-9f01-2b3c4d5e6f70", "root_run_id": "3c9e1f20-6a7b-4d8e-9f01-2b3c4d5e6f70", "conversation_id": "8a1b2c3d-4e5f-4a6b-8c7d-9e0f1a2b3c4d", "label": "Session", "tier": "pro", "effort": "medium", "source": "api", "status": "done", "finished": true, "waiting_on_you": null, "answer": "Found 4 broken links. The fixes are in fixes.md.", "error": null, "suggestions": [], "tasks": [ { "task_id": "5d6e7f80-1a2b-4c3d-8e9f-0a1b2c3d4e5f", "subject": "Crawl the site", "status": "completed", "active_form": null } ], "questions": [], "approvals": [], "files": [ { "file_id": "6e7f8091-2b3c-4d4e-9f0a-1b2c3d4e5f60", "path": "fixes.md", "name": "fixes.md", "mime": "text/markdown", "version": 1, "bytes": 2140, "origin": "agent", "updated_at": "2026-10-15T14:03:11Z" } ], "credits_spent": 412, "credits_metered": 412, "agents": 1, "turns": 6, "tokens": { "input": 14820, "output": 1650 }, "created_at": "2026-10-15T14:01:02Z", "started_at": "2026-10-15T14:01:03Z", "finished_at": "2026-10-15T14:03:12Z", "updated_at": "2026-10-15T14:03:12Z" } ``` #### ops.list_sessions `GET https://boldstudios.io/api/v1/ops/sessions` Scope `ops:read`. free. Your agent sessions Your most recent agent sessions, newest first, each with its title, status, tier and credits spent. limit defaults to 25, at most 100. ```bash curl 'https://boldstudios.io/api/v1/ops/sessions?limit=10' -H "Authorization: Bearer $BOLD_API_KEY" ``` Returns: ```json { "sessions": [ { "run_id": "3c9e1f20-6a7b-4d8e-9f01-2b3c4d5e6f70", "conversation_id": "8a1b2c3d-4e5f-4a6b-8c7d-9e0f1a2b3c4d", "title": "Broken link audit", "prompt_preview": "Audit https://example.com for broken links", "tier": "pro", "status": "done", "finished": true, "waiting_on_you": null, "source": "api", "credits_spent": 412, "created_at": "2026-10-15T14:01:02Z", "finished_at": "2026-10-15T14:03:12Z" } ] } ``` #### ops.steer `POST https://boldstudios.io/api/v1/ops/sessions/{run_id}/steer` Scope `ops:write`. The agent's work is billed in plan credits by tokens used: 10 credits ($0.01) per 1k tokens on Haiku, 25 credits ($0.025) on Sonnet, 100 credits ($0.10) on Opus. Steer a running session Sends a message into a session that is still working, like typing into it in BOLD Studios. On a session waiting for an answer it answers in free text; on one waiting for an approval it denies what was asked and carries on with the message. A finished session answers 409: start a new one with the same conversation_id instead. ```bash curl -X POST 'https://boldstudios.io/api/v1/ops/sessions/{run_id}/steer' \ -H "Authorization: Bearer $BOLD_API_KEY" \ -H 'Content-Type: application/json' \ -d '{"message":"Skip the blog, only check the product pages."}' ``` Returns: ```json { "ok": true, "run_id": "3c9e1f20-6a7b-4d8e-9f01-2b3c4d5e6f70" } ``` #### ops.stop `POST https://boldstudios.io/api/v1/ops/sessions/{run_id}/stop` Scope `ops:write`. free. Stop a session Stops a running session and every sub agent it started. Credits already used stay used; nothing more is charged once it stops. Stopping a finished session changes nothing. ```bash curl 'https://boldstudios.io/api/v1/ops/sessions/{run_id}/stop' -H "Authorization: Bearer $BOLD_API_KEY" ``` Returns: ```json { "ok": true, "run_id": "3c9e1f20-6a7b-4d8e-9f01-2b3c4d5e6f70", "status": "stopping" } ``` #### ops.answer `POST https://boldstudios.io/api/v1/ops/sessions/{run_id}/answer` Scope `ops:write`. The agent's work is billed in plan credits by tokens used: 10 credits ($0.01) per 1k tokens on Haiku, 25 credits ($0.025) on Sonnet, 100 credits ($0.10) on Opus. Answer a session's question Answers an open question from ops.get_session. answers maps each question's text to a string, or a list of strings for a multi select question. The session carries on at once. ```bash curl -X POST 'https://boldstudios.io/api/v1/ops/sessions/{run_id}/answer' \ -H "Authorization: Bearer $BOLD_API_KEY" \ -H 'Content-Type: application/json' \ -d '{"question_id":"7f8091a2-3c4d-4e5f-8a0b-1c2d3e4f5a61","answers":{"Which domain should I check?":"example.com"}}' ``` Returns: ```json { "ok": true, "run_id": "3c9e1f20-6a7b-4d8e-9f01-2b3c4d5e6f70", "question_id": "7f8091a2-3c4d-4e5f-8a0b-1c2d3e4f5a61" } ``` #### ops.approve `POST https://boldstudios.io/api/v1/ops/sessions/{run_id}/approve` Scope `ops:write`. The agent's work is billed in plan credits by tokens used: 10 credits ($0.01) per 1k tokens on Haiku, 25 credits ($0.025) on Sonnet, 100 credits ($0.10) on Opus. Allow or deny a tool call Decides a pending approval from ops.get_session: the agent asking before it runs a tool that changes something. decision is allow or deny; scope session allows that tool for the rest of the session. REST only, never an MCP tool. ```bash curl -X POST 'https://boldstudios.io/api/v1/ops/sessions/{run_id}/approve' \ -H "Authorization: Bearer $BOLD_API_KEY" \ -H 'Content-Type: application/json' \ -d '{"approval_id":"8091a2b3-4d5e-4f6a-9b0c-1d2e3f4a5b62","decision":"allow","scope":"once"}' ``` Returns: ```json { "ok": true, "run_id": "3c9e1f20-6a7b-4d8e-9f01-2b3c4d5e6f70", "approval_id": "8091a2b3-4d5e-4f6a-9b0c-1d2e3f4a5b62", "status": "allowed", "scope": "once", "waiting_on": 0 } ``` ### BOLD Sites Read and edit the live content of a BOLD Studios site, then publish it. Drafts and live values are separate, every publish is a version, and any version can be put back. Free: the site platform is paid for by its care plan, not per call. #### site.list_projects `GET https://boldstudios.io/api/v1/site/projects` Scope `site:read`. free. Every site you can edit The sites this key may work on, with how many fields each has and how many are waiting to be published. Start here: nothing else in this product works without a project id. ```bash curl 'https://boldstudios.io/api/v1/site/projects' -H "Authorization: Bearer $BOLD_API_KEY" ``` Returns: ```json { "projects": [ { "project_id": "6d0d810c-6e9e-4304-a0dd-0ed1988f3eeb", "name": "Open Hands Ministry", "site_url": "https://openhandsministry.example", "status": "live", "locales": [ "en" ], "fields": 24, "unpublished": 2, "last_published_at": "2026-09-04T18:22:10Z" } ] } ``` #### site.get_content `GET https://boldstudios.io/api/v1/site/projects/{project_id}/content` Scope `site:read`. free. The editable fields of a site Every editable field, draft and live side by side, with the label, group and character limit the site itself registered. This is the schema and the content in one call, so an agent can edit a page without being told the shape first. ```bash curl 'https://boldstudios.io/api/v1/site/projects/{project_id}/content?locale=en' -H "Authorization: Bearer $BOLD_API_KEY" ``` Returns: ```json { "project_id": "6d0d810c-6e9e-4304-a0dd-0ed1988f3eeb", "locale": "en", "unpublished": 1, "fields": [ { "key": "programs.hero.title", "kind": "text", "label": "Programmes headline", "group": "Programmes", "help_text": "Shown at the top of the programmes page.", "max_length": 80, "draft": "Serving our neighbours every week", "live": "Serving our neighbours", "changed": true, "published_at": "2026-09-04T18:22:10Z" } ] } ``` #### site.save_content `POST https://boldstudios.io/api/v1/site/projects/{project_id}/content` Scope `site:write`. free. Save drafts Saves values as drafts. Nothing here reaches a visitor: publishing is a separate call, on purpose, so an agent can write a whole page and a person can still be the one who decides it goes live. A key the site has not registered is still saved, because a site can add a field before it next boots. ```bash curl -X POST 'https://boldstudios.io/api/v1/site/projects/{project_id}/content' \ -H "Authorization: Bearer $BOLD_API_KEY" \ -H 'Content-Type: application/json' \ -d '{"locale":"en","edits":{"programs.hero.title":"Serving our neighbours every week","programs.hero.body":"Hot meals on Tuesdays and Fridays, no questions asked."}}' ``` Returns: ```json { "saved": 2, "unpublished": 2 } ``` #### site.publish `POST https://boldstudios.io/api/v1/site/projects/{project_id}/publish` Scope `site:write`. free. Make the drafts live Copies every changed draft onto the live site and records the result as a numbered version, so any publish can be put back later. Publishing with nothing changed is not an error, it answers published 0. ```bash curl -X POST 'https://boldstudios.io/api/v1/site/projects/{project_id}/publish' \ -H "Authorization: Bearer $BOLD_API_KEY" \ -H 'Content-Type: application/json' \ -d '{"note":"Autumn programme times"}' ``` Returns: ```json { "published": 2, "version": 7, "version_id": "e21a6273-94f4-43b9-ab71-c4b56dac3a9e" } ``` #### site.discard `POST https://boldstudios.io/api/v1/site/projects/{project_id}/discard` Scope `site:write`. free. Throw the drafts away Puts every draft back to what visitors are already seeing. The undo for a save that should not have happened, and it cannot touch the live site. ```bash curl 'https://boldstudios.io/api/v1/site/projects/{project_id}/discard' -H "Authorization: Bearer $BOLD_API_KEY" ``` Returns: ```json { "discarded": 2 } ``` #### site.versions `GET https://boldstudios.io/api/v1/site/projects/{project_id}/versions` Scope `site:read`. free. What has been published Published versions, newest first, so a caller can pick one to go back to. ```bash curl 'https://boldstudios.io/api/v1/site/projects/{project_id}/versions?limit=5' -H "Authorization: Bearer $BOLD_API_KEY" ``` Returns: ```json { "versions": [ { "version": 7, "note": "Autumn programme times", "keys_changed": 2, "created_at": "2026-09-05T09:14:02Z" }, { "version": 6, "note": null, "keys_changed": 1, "created_at": "2026-08-28T16:40:55Z" } ] } ``` #### site.rollback `POST https://boldstudios.io/api/v1/site/projects/{project_id}/rollback` Scope `site:write`. free. Put an earlier version back Restores a published version to both live and draft. It is recorded as a NEW version rather than by rewinding history, so the fact somebody rolled back is itself in the record and can be rolled back in turn. ```bash curl -X POST 'https://boldstudios.io/api/v1/site/projects/{project_id}/rollback' \ -H "Authorization: Bearer $BOLD_API_KEY" \ -H 'Content-Type: application/json' \ -d '{"version":6}' ``` Returns: ```json { "restored_from": 6, "version": 8, "keys_changed": 2 } ``` #### site.status `GET https://boldstudios.io/api/v1/site/projects/{project_id}/status` Scope `site:read`. free. Whether the site is live and editable Hosting and editing state, and when either is due to pause if the account is behind. A site that has gone quiet always says why rather than simply failing. ```bash curl 'https://boldstudios.io/api/v1/site/projects/{project_id}/status' -H "Authorization: Bearer $BOLD_API_KEY" ``` Returns: ```json { "project_id": "6d0d810c-6e9e-4304-a0dd-0ed1988f3eeb", "state": "active", "hosting_live": true, "marketing_allowed": true, "editing_allowed": true, "at_risk": false, "past_due_since": null, "days_past_due": 0, "enforced": true } ``` ### BOLD Strategy Competitive research, gap analysis, milestones, coaching and business plans for any person, artist, business, product or ministry. Profiles you own and profiles shared with your brand. Research and analysis run in the background and use BOLD Studios credits only when they succeed. #### strategy.list_profiles `GET https://boldstudios.io/api/v1/strategy/profiles` Scope `strategy:read`. free. Your strategy profiles Every BOLD Strategy profile you own, plus the ones a teammate shared with a brand you belong to: subject, lens, goal, brand, whether you own it, competitor count, open gaps and actions, and the latest trajectory read and headline. ```bash curl 'https://boldstudios.io/api/v1/strategy/profiles' -H "Authorization: Bearer $BOLD_API_KEY" ``` Returns: ```json { "profiles": [ { "profile_id": "06005fe0-523c-4808-b8da-3cd77908fea4", "title": "BOLD Send ยท Email platform", "lens": "Email platform", "goal": "Become the first choice for faith-driven brands sending email", "status": "active", "subject": { "entity_id": "3f1c2a9e-1b7d-4c55-9e0a-6a2f8d4b7c10", "name": "BOLD Send", "kind": "product" }, "parent_id": null, "brand": { "brand_id": "7263381e-0611-491a-8672-72fbdf22d6e4", "name": "BOLD Studios" }, "is_owner": true, "competitors": 4, "open_gaps": 5, "open_actions": 12, "trajectory": "at_risk", "headline": "Strong on price and voice, behind on deliverability proof and integrations.", "researched_at": "2026-10-07T18:40:00Z", "analyzed_at": "2026-10-07T19:02:00Z", "updated_at": "2026-10-07T19:02:00Z" } ] } ``` #### strategy.create_profile `POST https://boldstudios.io/api/v1/strategy/profiles` Scope `strategy:write`. free. Create a strategy profile One subject seen through one lens, for example an artist as a musician, or a product against its category. parent_id puts it under another profile you can see; brand_id shares it with a brand you belong to, and a profile under a shared parent is shared with the same brand by default. Counts against the plan's profile limit. ```bash curl -X POST 'https://boldstudios.io/api/v1/strategy/profiles' \ -H "Authorization: Bearer $BOLD_API_KEY" \ -H 'Content-Type: application/json' \ -d '{"subject_name":"BOLD Send","subject_kind":"product","website":"https://boldstudios.io/products/send","lens":"Email platform","goal":"Become the first choice for faith-driven brands sending email","horizon_months":12}' ``` Returns: ```json { "profile_id": "06005fe0-523c-4808-b8da-3cd77908fea4", "created": true } ``` #### strategy.update_profile `POST https://boldstudios.io/api/v1/strategy/profiles/{profile_id}` Scope `strategy:write`. free. Change, move or archive a profile Changes the title, lens, goal, stage, horizon or private notes, and the order among its siblings. The owner can also move it under any profile they can open (never under itself or its own sub-profiles), back to the top level with parent_id null, archive or restore it, or share it with a brand. No worked example yet. `GET https://boldstudios.io/api/v1` carries the current shape. #### strategy.get_profile `GET https://boldstudios.io/api/v1/strategy/profiles/{profile_id}` Scope `strategy:read`. free. Read a whole profile The subject and every competitor with their researched facts and sources, the comparison dimensions and scores, findings (gaps, whitespace, strengths, double downs, threats, patterns), milestones, daily and weekly actions, the latest plan, the coach conversation, the plan limits and the credits each action uses. No worked example yet. `GET https://boldstudios.io/api/v1` carries the current shape. #### strategy.network `GET https://boldstudios.io/api/v1/strategy/profiles/{profile_id}/network` Scope `strategy:read`. free. Roll up a whole network The profile and every sub-profile under it, at any depth, in the order arranged: trajectory, headline, first move, competitor and open counts, and up to 40 of each profile's strongest open findings. kind=upgrade returns the build list for the whole network. No worked example yet. `GET https://boldstudios.io/api/v1` carries the current shape. #### strategy.search_library `GET https://boldstudios.io/api/v1/strategy/library` Scope `strategy:read`. free. Search the shared research library People, companies and products already researched in the BOLD library, with how many sourced facts each has and whether the research is fresh. Adding a fresh one to a profile and researching it is free. ```bash curl 'https://boldstudios.io/api/v1/strategy/library' -H "Authorization: Bearer $BOLD_API_KEY" ``` Returns: ```json { "query": "mailchimp", "entities": [ { "entity_id": "3f1c2a9e-1b7d-4c55-9e0a-6a2f8d4b7c10", "name": "Mailchimp", "kind": "product", "website": "https://mailchimp.com", "summary": "Email marketing and automation platform for small and medium businesses, owned by Intuit since 2021.", "stage": "Estimated $800M annual revenue at the 2021 acquisition", "researched_at": "2026-10-07T18:52:10Z", "fresh": true, "facts": 45 } ] } ``` #### strategy.add_competitor `POST https://boldstudios.io/api/v1/strategy/profiles/{profile_id}/competitors` Scope `strategy:write`. free. Add a competitor Adds a person, company or product to the profile's field. Research lives in a shared library keyed by website (or kind and name), so a competitor someone already researched is free until it goes stale. Adding one that is already there updates its tier and note. ```bash curl -X POST 'https://boldstudios.io/api/v1/strategy/profiles/{profile_id}/competitors' \ -H "Authorization: Bearer $BOLD_API_KEY" \ -H 'Content-Type: application/json' \ -d '{"name":"Mailchimp","kind":"platform","website":"https://mailchimp.com","tier":"leader"}' ``` Returns: ```json { "profile_id": "06005fe0-523c-4808-b8da-3cd77908fea4", "entity_id": "3f1c2a9e-1b7d-4c55-9e0a-6a2f8d4b7c10", "added": true } ``` #### strategy.add_competitors `POST https://boldstudios.io/api/v1/strategy/profiles/{profile_id}/competitors/bulk` Scope `strategy:write`. free. Add many competitors at once Up to 100 at a time, from text (links or names, one per line, or many links on a line, with an optional tier after a comma) or from items. Names come from the link when not given. Without a tier, BOLD Strategy places each one on the ladder at the next comparison. Returns added, existing and not added, and the result for every line. No worked example yet. `GET https://boldstudios.io/api/v1` carries the current shape. #### strategy.add_note `POST https://boldstudios.io/api/v1/strategy/profiles/{profile_id}/notes` Scope `strategy:write`. free. Give the analysis context Creates a private note, or edits one with note_id: product docs, pricing, customer feedback, the roadmap, numbers. Every analysis, the coach and the plan treat it as ground truth. share_down makes every sub-profile underneath read it too. Up to 50 notes of 20,000 characters. No worked example yet. `GET https://boldstudios.io/api/v1` carries the current shape. #### strategy.research `POST https://boldstudios.io/api/v1/strategy/profiles/{profile_id}/research` Scope `strategy:write`. 500 credits ($0.50) per new competitor, 300 credits ($0.30) per refresh, free when the shared library already has it fresh. Research the field With entity_id, researches that one member. Without it, starts research on up to six members that are not fresh in the shared library and says how many remain. Fresh members are free; a first pass and a refresh use the credits in rates. Answers 202 with run ids. ```bash curl -X POST 'https://boldstudios.io/api/v1/strategy/profiles/{profile_id}/research' \ -H "Authorization: Bearer $BOLD_API_KEY" \ -H 'Content-Type: application/json' \ -d '{}' ``` Returns: ```json { "status": "started", "runs": [ { "entity_id": "3f1c2a9e-1b7d-4c55-9e0a-6a2f8d4b7c10", "name": "Mailchimp", "run_id": "b84e1f02-6c3a-4d9b-8f27-1e5a0c9d3b66" } ], "cached": [], "refused": [], "remaining": 0 } ``` #### strategy.analyze `POST https://boldstudios.io/api/v1/strategy/profiles/{profile_id}/analyze` Scope `strategy:write`. 250 credits ($0.25) to compare, 60 credits ($0.06) per re-scored competitor, 400 credits ($0.40) for the path; free when nothing changed. Compare, then find the path stage compare scores the subject and every competitor on the dimensions that decide this field and reads the trajectory. Once a profile is compared, stage compare re-scores only what changed since the last scoring (new competitors, refreshed research, edited notes or private facts, new or reworded dimensions), charged per member re-scored and never more than a full comparison, and answers status current, free, when nothing changed. Pass rebuild true to pick the dimensions again and score the whole field. stage path, after compare, writes the gaps, whitespace, patterns, milestones and daily and weekly actions. Hand-made scores and findings you accepted are kept. Answers 202 with a run id. ```bash curl -X POST 'https://boldstudios.io/api/v1/strategy/profiles/{profile_id}/analyze' \ -H "Authorization: Bearer $BOLD_API_KEY" \ -H 'Content-Type: application/json' \ -d '{"stage":"compare"}' ``` Returns: ```json { "stage": "compare", "status": "started", "run_id": "b84e1f02-6c3a-4d9b-8f27-1e5a0c9d3b66" } ``` #### strategy.save_update `POST https://boldstudios.io/api/v1/strategy/profiles/{profile_id}/updates` Scope `strategy:write`. free. Plan, build and ship revisions The revisions queue. finding_ids alone sends those findings to the queue as planned revisions (never twice). A title creates a revision; update_id changes one or moves it through planned, building, shipped or dropped. A body holds up to 200,000 characters, so a whole sheet of notes fits in one revision. Shipping marks its findings done, and the next analysis judges the subject with every revision shipped since the last scan in place. No worked example yet. `GET https://boldstudios.io/api/v1` carries the current shape. #### strategy.build_file `GET https://boldstudios.io/api/v1/strategy/profiles/{profile_id}/build-file` Scope `strategy:read`. free. Download a profile's build file Every open revision on the profile, and unless tree=false on every sub-profile under it, as one Markdown file for a Claude or BOLD Ops session: the repo on line one, PLANNING ONLY on line two when the work is not paid for, the standing notes, then the revisions grouped by profile in priority order, and the line that marks them shipped. ids (comma separated) limits it to chosen revisions. The same file the app downloads. ```bash curl 'https://boldstudios.io/api/v1/strategy/profiles/{profile_id}/build-file' -H "Authorization: Bearer $BOLD_API_KEY" ``` Returns: ```json { "profile_id": "06005fe0-523c-4808-b8da-3cd77908fea4", "filename": "bold-send--revisions--2026-10-10.md", "markdown": "Repo: https://github.com/BOLDStudios/bold-studios\n\n# BOLD Send: 3 revisions\n\n## 1. Deliverability page with real inbox placement numbers\n- Add the placement chart\n- Link it from pricing\n", "ids": [ "9b2f3c1a-4d5e-4f60-8a7b-1c2d3e4f5a6b" ], "count": 3, "profiles": 1, "planning_only": 0, "tasks": 7 } ``` #### strategy.set_revisions `POST https://boldstudios.io/api/v1/strategy/revisions/status` Scope `strategy:write`. free. Move many revisions at once Moves up to 500 revisions on profiles you can open to planned, building, shipped or dropped. Shipping records note as what shipped and marks the revisions' findings done. Returns how many changed. ```bash curl -X POST 'https://boldstudios.io/api/v1/strategy/revisions/status' \ -H "Authorization: Bearer $BOLD_API_KEY" \ -H 'Content-Type: application/json' \ -d '{"ids":["9b2f3c1a-4d5e-4f60-8a7b-1c2d3e4f5a6b"],"status":"shipped","note":"Deliverability page is live"}' ``` Returns: ```json { "changed": 1, "status": "shipped" } ``` #### strategy.build_settings `POST https://boldstudios.io/api/v1/strategy/profiles/{profile_id}/build-settings` Scope `strategy:write`. free. Set a profile's build settings What every build file for the profile carries: repo_url (a link or owner/repo), build_notes for every session, phase (active, upcoming, pending or paused), build_cleared (false keeps it plan only until paid) and pitch_notes. Sub-profiles inherit the repo, the notes and the plan-only gate. An empty string clears a setting. ```bash curl -X POST 'https://boldstudios.io/api/v1/strategy/profiles/{profile_id}/build-settings' \ -H "Authorization: Bearer $BOLD_API_KEY" \ -H 'Content-Type: application/json' \ -d '{"repo_url":"BOLDStudios/bold-studios","phase":"active","build_cleared":true,"build_notes":"Vite, React, TypeScript, Tailwind. No em dashes in any copy."}' ``` Returns: ```json { "profile_id": "06005fe0-523c-4808-b8da-3cd77908fea4", "saved": true, "settings": { "repo_url": "https://github.com/BOLDStudios/bold-studios", "cleared": true } } ``` #### strategy.waves `GET https://boldstudios.io/api/v1/strategy/waves` Scope `strategy:read`. free. List revision waves Every open revision across your profiles grouped into themes (pricing, proof, trust, positioning, integrations and more), most common first, with how many products each spans and the root profiles (repos) that hold them. Wave 1 is built once and applied everywhere, wave 2 is shared infrastructure, wave 3 is product specific, wave 0 is research context. root limits it to one profile tree. ```bash curl 'https://boldstudios.io/api/v1/strategy/waves' -H "Authorization: Bearer $BOLD_API_KEY" ``` Returns: ```json { "waves": [ { "theme": "pricing_terms", "label": "Public pricing, terms and limits", "wave": 1, "lane": "code", "approach": "Build ONE shared pricing block for the product page template...", "open": 150, "profiles": 50, "roots": [ { "root_id": "06005fe0-523c-4808-b8da-3cd77908fea4", "title": "BOLD Studios", "open": 93 } ] } ] } ``` #### strategy.wave_file `GET https://boldstudios.io/api/v1/strategy/waves/{theme}/file` Scope `strategy:read`. free. Download a wave build file One theme as a single Markdown build file for a Claude or BOLD Ops session. Without root it holds every platform, each in its own section with its repo, plan-only gate and revision ids; with root, just that platform. Build the shared piece once, apply it product by product, then mark the theme shipped everywhere. ```bash curl 'https://boldstudios.io/api/v1/strategy/waves/{theme}/file' -H "Authorization: Bearer $BOLD_API_KEY" ``` Returns: ```json { "theme": "pricing_terms", "root_id": "06005fe0-523c-4808-b8da-3cd77908fea4", "filename": "bold-wave1--pricing-terms--bold-studios--2026-10-14.md", "markdown": "Repo: https://github.com/BOLDStudios/bold-studios\n\n# BOLD Studios and its products: wave 1, Public pricing, terms and limits\n", "build_count": 93, "profiles": 32 } ``` #### strategy.start_wave `POST https://boldstudios.io/api/v1/strategy/waves/{theme}/start` Scope `strategy:write`. Each pack is built by BOLD Ops and billed by tokens used: 25 credits ($0.025) per 1k tokens on Sonnet, 100 credits ($0.10) on Opus. Start building a wave Cuts one theme under one root profile into packs and has BOLD Ops open a pull request for each on the root's repo. Pack 1 builds the shared piece; the rest start once it merges. Merging a pack marks its revisions shipped on every profile; a closed or failed pack puts them back. Plan-only (unpaid) profiles are never built. Returns the batch to follow with strategy.wave_runs. ```bash curl -X POST 'https://boldstudios.io/api/v1/strategy/waves/{theme}/start' \ -H "Authorization: Bearer $BOLD_API_KEY" \ -H 'Content-Type: application/json' \ -d '{"root":"06005fe0-523c-4808-b8da-3cd77908fea4"}' ``` Returns: ```json { "batch_id": "b84e1f02-6c3a-4d9b-8f27-1e5a0c9d3b66", "packs": 6, "revisions": 93, "repo": "BOLDStudios/bold-studios" } ``` #### strategy.run_status `GET https://boldstudios.io/api/v1/strategy/profiles/{profile_id}/runs/{run_id}` Scope `strategy:read`. free. Check a run Whether a research, analysis or plan run is running, ok or failed, the credits it used, and the error when it failed. A run still marked running after ten minutes reads as failed and was not charged. ```bash curl 'https://boldstudios.io/api/v1/strategy/profiles/{profile_id}/runs/{run_id}' -H "Authorization: Bearer $BOLD_API_KEY" ``` Returns: ```json { "id": "b84e1f02-6c3a-4d9b-8f27-1e5a0c9d3b66", "action": "analyze_compare", "entity_id": null, "credits": 150, "status": "ok", "error": null, "started_at": "2026-10-07T19:00:10Z", "finished_at": "2026-10-07T19:01:52Z" } ``` #### strategy.wave_runs `GET https://boldstudios.io/api/v1/strategy/wave-runs` Scope `strategy:read`. free. Check wave progress Your wave settings (auto, parallel builds, daily cap) and every recent wave batch with each pack's status (waiting, queued, building, pr_open, shipped, no_change, failed, cancelled) and pull request link. ```bash curl 'https://boldstudios.io/api/v1/strategy/wave-runs' -H "Authorization: Bearer $BOLD_API_KEY" ``` Returns: ```json { "settings": { "auto": false, "max_parallel": 2, "daily_cap": 12, "paused": false }, "dispatched_today": 1, "batches": [ { "batch_id": "b84e1f02-6c3a-4d9b-8f27-1e5a0c9d3b66", "theme": "pricing_terms", "label": "Public pricing, terms and limits", "wave": 1, "root": "BOLD Studios", "repo": "BOLDStudios/bold-studios", "revisions": 93, "shipped": 0, "packs": [ { "pack_no": 1, "status": "pr_open", "count": 16, "pr_url": "https://github.com/BOLDStudios/bold-studios/pull/900" } ] } ] } ``` #### strategy.stop_wave `POST https://boldstudios.io/api/v1/strategy/wave-runs/{batch_id}/stop` Scope `strategy:write`. free. Stop a wave Stops a wave batch. Packs not yet started are cancelled and their revisions go back on the board; packs already building or in review finish on their own. ```bash curl 'https://boldstudios.io/api/v1/strategy/wave-runs/{batch_id}/stop' -H "Authorization: Bearer $BOLD_API_KEY" ``` Returns: ```json { "batch_id": "b84e1f02-6c3a-4d9b-8f27-1e5a0c9d3b66", "cancelled": 5 } ``` #### strategy.ship_wave `POST https://boldstudios.io/api/v1/strategy/waves/{theme}/ship` Scope `strategy:write`. free. Ship a whole wave Marks every open revision in one theme shipped on every profile you can open (optionally under one root), closes their findings, and holds back any revision ids listed in except. Use it after building a wave yourself from its file. ```bash curl -X POST 'https://boldstudios.io/api/v1/strategy/waves/{theme}/ship' \ -H "Authorization: Bearer $BOLD_API_KEY" \ -H 'Content-Type: application/json' \ -d '{"root":"06005fe0-523c-4808-b8da-3cd77908fea4","note":"Shared pricing block live on every product page","except":[]}' ``` Returns: ```json { "theme": "pricing_terms", "shipped": 93, "profiles": 32, "held": 0 } ``` #### strategy.wave_group_file `GET https://boldstudios.io/api/v1/strategy/wave-groups/{wave}/file` Scope `strategy:read`. free. Download a whole wave A whole wave (1 build once, 2 shared infrastructure, 3 product specific) as ONE Markdown build file: every theme in the wave, most common first, with every platform and product inside it, the session rules, the report shape and the one line that ships the whole wave. ```bash curl 'https://boldstudios.io/api/v1/strategy/wave-groups/{wave}/file' -H "Authorization: Bearer $BOLD_API_KEY" ``` Returns: ```json { "wave": 1, "filename": "bold-wave1--all-items--2026-10-15.md", "markdown": "# BOLD Strategy, wave 1: build once\n\n## Items (10)\n\n1. Public pricing, terms and limits: 151 revisions across 49 profiles on 7 platforms\n", "items": 10, "revisions": 576, "profiles": 53, "platforms": 10, "build_count": 576 } ``` #### strategy.ship_wave_group `POST https://boldstudios.io/api/v1/strategy/wave-groups/{wave}/ship` Scope `strategy:write`. free. Ship every theme in a wave Marks every open revision in a whole wave (every theme) shipped on every profile you can open, closes their findings, and holds back any revision ids listed in except. A gap a later scoring run finds again comes back as a new revision and rejoins its wave. ```bash curl -X POST 'https://boldstudios.io/api/v1/strategy/wave-groups/{wave}/ship' \ -H "Authorization: Bearer $BOLD_API_KEY" \ -H 'Content-Type: application/json' \ -d '{"note":"Wave 1 live on every platform","except":[]}' ``` Returns: ```json { "wave": 1, "shipped": 576, "profiles": 53, "held": 0 } ``` #### strategy.suggest `POST https://boldstudios.io/api/v1/strategy/profiles/{profile_id}/suggest` Scope `strategy:write`. 150 credits ($0.15) per call. Who to study Nine to twelve real, active leaders, players a few steps ahead, peers and adjacent players this profile should study, each with a website and one line on what to learn. Adds nothing to the profile. Answers in the same call, usually within a minute. ```bash curl 'https://boldstudios.io/api/v1/strategy/profiles/{profile_id}/suggest' -H "Authorization: Bearer $BOLD_API_KEY" ``` Returns: ```json { "status": "ok", "credits": 60, "suggestions": [ { "name": "Kit", "kind": "platform", "website": "https://kit.com", "tier": "ahead", "why": "Grew by serving creators first, the same lane BOLD Send can own for ministries." } ] } ``` #### strategy.coach `POST https://boldstudios.io/api/v1/strategy/profiles/{profile_id}/coach` Scope `strategy:write`. 100 credits ($0.10) per reply. Ask the strategy coach One message to a coach that has read the whole profile, its competitors' real trajectories and its open actions. The exchange is saved to the profile's conversation. ```bash curl -X POST 'https://boldstudios.io/api/v1/strategy/profiles/{profile_id}/coach' \ -H "Authorization: Bearer $BOLD_API_KEY" \ -H 'Content-Type: application/json' \ -d '{"message":"What should I do this week?"}' ``` Returns: ```json { "status": "ok", "credits": 60, "reply": "Ship the deliverability page with your real inbox placement numbers. It is the gap every comparison flagged first." } ``` #### strategy.plan `POST https://boldstudios.io/api/v1/strategy/profiles/{profile_id}/plan` Scope `strategy:write`. 400 credits ($0.40) per plan. Write the strategy and business plan Writes a new version of the profile's strategy and business plan in Markdown: the short version, where it stands, the field, its edge, gaps, whitespace, offer, go to market, the path, operating rhythm, risks and the next 90 days. Needs the comparison first. Answers 202 with a run id; read the plan with strategy.get_profile. ```bash curl 'https://boldstudios.io/api/v1/strategy/profiles/{profile_id}/plan' -H "Authorization: Bearer $BOLD_API_KEY" ``` Returns: ```json { "status": "started", "run_id": "b84e1f02-6c3a-4d9b-8f27-1e5a0c9d3b66" } ``` ### BOLD QA The BOLD Test Lab on any public page: real browsers on phones, a tablet, a laptop and a desktop, in light and dark, checking axe accessibility, LCP, CLS and JavaScript weight, broken links and images, SEO basics, overflow, tap targets and copy. Runs take a few minutes and use BOLD Studios credits only when they finish with a verdict. #### qa.run `POST https://boldstudios.io/api/v1/qa/runs` Scope `qa:write`. 500 credits ($0.50) per run, charged only when the run reaches a verdict. Test a page Starts a QA run on one public https page and answers 202 with the run id, whether it was dispatched or queued, the address as it will be tested, and the price of a run in credits. One run at a time per account (409 conflict while one is going) and up to 30 a day (429 rate_limited). Read the verdict and findings with qa.get. Credits are charged only when the run finishes with a verdict; a run that errors costs nothing. ```bash curl -X POST 'https://boldstudios.io/api/v1/qa/runs' \ -H "Authorization: Bearer $BOLD_API_KEY" \ -H 'Content-Type: application/json' \ -d '{"url":"https://example.com"}' ``` Returns: ```json { "run_id": "3f6d2a90-8c1e-4b7a-9e25-7d0b4c6a1e58", "status": "dispatched", "url": "https://example.com/", "price_credits": 500 } ``` #### qa.get `GET https://boldstudios.io/api/v1/qa/runs/{run_id}` Scope `qa:read`. free. One QA run and its findings The run (address, status, verdict pass, warn or fail, counts by severity, error, credits priced and charged, and when it was created, started and finished) and every finding, most severe first, with its check, severity, title, message, count and where it was seen. A run that is not yours reads as not found. ```bash curl 'https://boldstudios.io/api/v1/qa/runs/{run_id}' -H "Authorization: Bearer $BOLD_API_KEY" ``` Returns: ```json { "run": { "id": "3f6d2a90-8c1e-4b7a-9e25-7d0b4c6a1e58", "url": "https://example.com/", "status": "warned", "verdict": "warn", "counts": { "critical": 0, "serious": 2, "moderate": 3, "minor": 1, "info": 0 }, "error": null, "credits_price": 500, "credits_charged": 500, "created_at": "2026-10-14T17:00:00Z", "started_at": "2026-10-14T17:00:20Z", "finished_at": "2026-10-14T17:03:41Z" }, "findings": [ { "check": "axe", "severity": "serious", "title": "Text without enough contrast", "message": "4 elements fall below the WCAG AA contrast ratio.", "count": 4, "where": [ "iPhone 15 / dark: footer a.small" ] } ] } ``` #### qa.list `GET https://boldstudios.io/api/v1/qa/runs` Scope `qa:read`. free. Your QA runs Your most recent QA runs, newest first, each with its address, status, verdict and counts, plus the current price of a run in credits. No worked example yet. `GET https://boldstudios.io/api/v1` carries the current shape.