standalone_sender on your channel’s descriptor and praisonai schedule tick delivers to it from OS cron, CI, or a scale-to-zero worker. You don’t need a gateway running.
Quick Start
1
Schedule an agent to a custom channel
Use the same Then run the tick from OS cron with no gateway running:
deliver token you would for a built-in platform. Only the channel name changes.2
Add a standalone_sender to the plugin's descriptor
Add one method to the descriptor your
praisonai.channels plugin already ships:praisonai schedule tick is a fresh process. A register_platform(..., descriptor=...) call in your own app code never runs there. Put the descriptor on the adapter class as channel_descriptor (an attribute or a callable), so the registry finds it when it loads your entry point.How It Works
Precedence
A bundled name always wins.
If the descriptor lookup raises (a broken plugin, for example), the resolver logs at debug level and returns
None. The run records delivery_error and the tick keeps going.
Sync or Async
Either form works. The executor awaits the result only when it is awaitable.- Async
- Sync
DeliveryTarget (target.channel, target.channel_id, target.thread_id) and the text to post.
Failure contract. Raise when the send fails: the run records
delivered=False with delivery_error=str(exc). If you return False, the run is also recorded as delivered=False. Any other return value, including None, counts as success. This matches the live delivery_handler contract in Scheduler Delivery → Failure modes.Retries are up to you. Each bundled sender has its own bounded in-process retry. A plugin sender doesn’t have to retry. If it raises on the first failure, that is correct behaviour: the run records
delivery_error and the tick moves on.End-to-end: cron deployment
1
Install the plugin and set its secret
2
Create a job with the custom-channel target
3
Schedule the tick in OS cron
4
Gateway off, cron fires, message lands
Cron fires, the job runs, the resolver falls through to your descriptor, and the brief arrives in
ops. Run history shows delivered=True.Best Practices
Keep the sender stateless
Keep the sender stateless
Don’t keep open sockets or cached connections. Make one push per call so the short-lived tick process exits cleanly.
Read secrets from env vars
Read secrets from env vars
The sender runs out of process, so the onboarding wizard never runs there. Every secret it needs should have an
env= fallback on its ChannelField.Let failures raise
Let failures raise
Don’t catch and swallow errors inside the sender. When an error reaches the executor, it records
delivery_error, and operators can see the real reason in run history.Prefer async for network I/O
Prefer async for network I/O
Both forms work. The executor runs on asyncio, so a sync sender blocks the event loop for as long as its HTTP call takes.
Related
Channel Descriptor
The full descriptor surface
Scheduler Delivery
The scheduled delivery pipeline
Custom Gateway Channel
Zero-code channel registration
Bot Platform Plugins
Entry-point registration
Availability. Introduced in PraisonAI PR #6100 (fixes #6099). Earlier versions needed a resident gateway for a custom channel to receive scheduled deliveries.

