Broadcast safely
Broadcast operations submit signed transactions to Blurt Layer 1. They can change account state, create content, vote, transfer funds, update custom JSON and perform other irreversible actions.
Use this guide before adding client.broadcast to an application. If you only need blockchain data, use Read chain data or Use Nexus social data instead.
The broadcast boundary
A safe application keeps three stages separate:
1. Query blockchain data
No private key. No chain change.
2. Build an operation
Local payload construction. Still no broadcast.
3. Sign and broadcast
Private key or wallet approval required. Real chain effect.Do not collapse these stages in user interfaces. Let users inspect what will be signed and broadcast.
Authority selection
Different operations require different authorities.
| Operation family | Typical authority | Examples |
|---|---|---|
| Social/content actions | posting | vote, comment, delete comment, follow, reblog, community subscribe |
| Funds and account-sensitive actions | active | transfer, vesting, delegation, savings, escrow, proposals |
| Witness and governance actions | active/owner by operation | witness vote/proxy/update, witness properties, proposal create/vote/remove |
| Account recovery / ownership changes | owner or recovery-account authority | recovery request, recovery confirmation, recovery-account change |
| Memo decryption/encryption | memo key | not a broadcast authority |
Use the least powerful authority that satisfies the operation. Read Accounts and authorities before asking users for keys.
Never hard-code private keys
Private keys are application secrets. Do not commit them, paste them into examples, or bake them into browser bundles.
Prefer:
- wallet integrations;
- user-controlled signing flows;
- environment variables for local scripts;
- encrypted stores for backend services;
- short-lived explicit approval flows.
Avoid:
- source-code key literals;
- logging private keys or signed payloads with secrets;
- asking for active/owner keys when posting authority is enough;
- browser signing flows without an XSS and supply-chain threat model.
Build before broadcasting
Use operation builders when your application needs previews, tests or external signing.
import { buildPostOperation } from '@beblurt/dblurt';
const operation = buildPostOperation({
author: 'alice',
title: 'Hello Blurt',
body: 'Body text',
tags: ['blurt', 'sdk']
});
console.log(operation);This creates a Layer 1 operation. It does not sign and does not broadcast.
Builders are useful when you want to show the exact operation payload before handing it to a wallet or signing workflow.
Broadcast a vote
This example shows the shape of a direct broadcast workflow. It assumes your application already has a safe key-handling path.
import { Client, PrivateKey } from '@beblurt/dblurt';
const client = new Client(['https://rpc.blurt.blog']);
const key = PrivateKey.fromString(process.env.BLURT_POSTING_KEY ?? '');
const confirmation = await client.broadcast.vote({
voter: 'alice',
author: 'bob',
permlink: 'hello-blurt',
weight: 10_000
}, key);
console.log(confirmation.id);For application code, validate all identities and the vote weight before signing.
Broadcast custom JSON
Social helpers such as follows, reblogs and community actions are custom_json operations.
const confirmation = await client.broadcast.follow('alice', 'bob', key);
console.log(confirmation.id);For custom application payloads, use customJson directly:
await client.broadcast.customJson({
required_auths: [],
required_posting_auths: ['alice'],
id: 'my-app',
json: JSON.stringify({ action: 'ping', ts: Date.now() })
}, key);Keep custom JSON payloads deterministic and inspectable before signing.
Broadcast helper families
client.broadcast provides first-class helpers for the Layer 1 operation families normal applications are likely to own:
| Workflow | Helpers |
|---|---|
| Content | comment(), commentOptions(), deleteComment() |
| Voting/social custom JSON | vote(), follow(), unfollow(), mute(), unmute(), reblog(), undoReblog(), community helpers |
| Funds and vesting | transfer(), transferToVesting(), withdrawVesting(), setWithdrawVestingRoute(), delegateVestingShares() |
| Savings | transferToSavings(), transferFromSavings(), cancelTransferFromSavings() |
| Escrow | escrowTransfer(), escrowApprove(), escrowDispute(), escrowRelease() |
| Account recovery | requestAccountRecovery(), recoverAccount(), changeRecoveryAccount() |
| Witness management | accountWitnessVote(), witnessUpdate(), witnessSetProperties() |
| Deprecated compatibility | accountWitnessProxy(), claimAccount(), createClaimedAccount() remain serializable, but current Blurt Layer 1 evaluators reject them after hardfork activation; see Deprecations. |
| Governance proposals | createProposal(), updateProposalVotes(), removeProposal() |
Use sendOperations() when you need to submit a valid Layer 1 operation that intentionally has no dedicated helper.
Prepare and sign without broadcasting
Some applications need to prepare and sign before submitting through a separate channel.
const transaction = await client.broadcast.prepareTransaction([
['vote', {
voter: 'alice',
author: 'bob',
permlink: 'hello-blurt',
weight: 10_000
}]
]);
const signed = client.broadcast.sign(transaction, key);
console.log(signed.signatures);This still handles sensitive signing material. Treat it as a high-risk path even if submission happens later.
Ambiguous failures
Broadcast failures are different from failed blockchain queries.
If a network timeout happens after submission, the transaction may have been:
- accepted and included;
- rejected by the node;
- still propagating;
- never received by the endpoint.
Applications should retain and surface enough local context to help users reconcile the result. Do not blindly retry a broadcast operation as if it were an ordinary data query.
When public RPC nodes expose transaction_status_api, use client.transactionStatus.findTransaction(confirmation.id) to check a transaction id after broadcast. Status lookup is an observation step; it does not make a retry automatically safe.
Timeout and retry semantics
Treat broadcast timeouts as ambiguous. A timeout after submission is not proof that the transaction failed and not proof that it succeeded.
Recommended application behavior:
- keep the locally generated transaction id;
- show the user that the result is unknown;
- query transaction status where the selected RPC endpoint exposes
transaction_status_api; - avoid automatic duplicate broadcasts unless the application has a protocol-aware idempotency plan.
client.transactionStatus.findTransaction(id) is an observation helper, not a retry policy. Applications still own duplicate-submission, UX and reconciliation decisions.
Before you broadcast
Check all of the following:
- The user intended the action.
- The operation payload is visible or otherwise auditable.
- The operation uses the least powerful authority.
- The signing key or wallet belongs to the expected account.
- The endpoint list is trusted.
- The UI can explain success, rejection and ambiguous network failures.
- The application has a plan for duplicate submissions and retries.
Related docs
- Accounts and authorities
- Blockchain queries vs broadcast operations
- Publish content
- Social actions
- Handle errors
- Security policy
API details
Use the API reference for exact method signatures and transaction types:
