Bot setup guide
Connect your trade to an n8n AI bot.
A step-by-step setup guide for bot builders. No coding needed. Import the ready-made template, or build the workflow yourself one node at a time.
What you will build
An n8n workflow that, each time a candle closes on your trade's timeframe:
- Gets the latest candles for your trade from Candleweb, with your open position and what a buy would do right now.
- Asks an AI what to do: buy, sell or hold.
- Sends that decision back to Candleweb, which acts on it when it clears your trade's bar.
The AI only sees the market and your position, and only ever gives its opinion: decision, confidence, risk and reason. The ids Candleweb needs back never reach the AI. They are copied over automatically from Candleweb's own response.
[On each candle close] → [Get Candles] → [Has Data?] → [Prepare for AI] → [AI Decision] → [Send Decision]
How Candleweb acts on a decision
| Decision | What happens |
|---|---|
| BUY | Candleweb opens a position at the market price, but only when the confidence is above your trade's minimum confidence (50 unless you change it) and the risk is LOW or MEDIUM. Candleweb sets the stop and the size from your trade's risk settings. Trading is long-only, and a BUY is refused while a position on the same market is already open. |
| SELL | Candleweb closes the trade's open position at the market price, but only when Let my bot's SELL close the position is on for the trade and the confidence clears the same bar. If it is off (the default), the SELL is only recorded, and positions close by their stop or trailing stop. A SELL never opens a short. |
| HOLD | Recorded. Nothing else happens. |
Every decision is recorded on the trade's decision timeline. Candleweb's reply to each decision says what it did and why (see Test, then switch on).
Part 1 · Set up in Candleweb
Step 1: Choose Demo or Live
Open Settings and check the Demo/Live switch. A trade runs in the mode the switch is set to when you create the trade, and it keeps that mode. Switching later only changes new trades. Leave it on Demo while you build and test.
Step 2: Create a bot
Go to Bots, create a new bot, and fill in:
| Setting | Choose |
|---|---|
| Name | Anything you like, for example My n8n bot |
| Platform | n8n Cloud or n8n self-hosted |
| Data delivery | My bot will pull. Push is not available yet. |
| Reply timeout | 60 seconds. An AI usually answers in 5 to 30 seconds. |
| Decision style | Buy / sell / hold or Buy / hold only. Candleweb does not act on Signal strength bots. |
| When there is no reply | Leave it on Skip the candle. |
Save, then copy the bot's developer key. It looks like cw_dev_3f9a1b2c....
Step 3: Create a trade and point it at your bot
Go to Trades, create a new trade, and fill in:
| Setting | What to know |
|---|---|
| Exchange account | Needed for a Live trade. A Demo trade can run without one. |
| Pair | The market to trade, for example BTCUSDT. |
| Timeframe | 1m, 5m, 15m, 1h or 4h. Do not choose 1 day: the candle feed does not support it yet. |
| Allocated amount | The money this trade may use. |
| Indicators | Pick them from the list. Your bot receives their values with every candle. |
| Candle history | How many candles your bot receives on each pull (20 by default). |
| Who decides | Your bot (not CandleAI), then choose the bot from Step 2. |
| Minimum confidence | Candleweb acts only on decisions with a confidence above this. At 50, a 50 is held. |
| Risk per position | "Lose at most X% of this trade's capital if a stop is hit" (0.1 to 25). |
| Max position size | "Use at most X% of this trade's capital per position" (1 to 100). |
| Trailing stop | On by default. The stop follows the price up as each candle closes. |
| Let my bot's SELL close the position | Off by default. Turn it on if you want your bot's SELL decisions to close positions. |
Save. The trade is active straight away. Creating it also gives your bot permission to open positions for your account, so it can trade as soon as you connect it.
Step 4: Copy the connection details
Open the trade and find Connect your bot. Copy these three:
| What | Method | Looks like |
|---|---|---|
| Pull URL | GET | https://api.candleweb.io/api/trading_v2/trades/42/candles/ |
| Decision URL | POST | https://api.candleweb.io/api/trading_v2/trades/42/decision/ |
| Developer key | cw_dev_3f9a1b2c... |
Use the URLs exactly as shown. 42 is your trade's number. Your bot sends the key on both calls in a header named X-Candleweb-Key.
Now either import the template (fastest) or build the workflow yourself.
Quick start
Use the workflow template
Skip the build: download our ready-made n8n workflow, import it, and fill in your own connection details from Step 4.

Step 1: Import the workflow into n8n
- Open your n8n dashboard (either n8n Cloud or your self-hosted n8n).
- Click Create Workflow or New Workflow.
- In the workflow editor, click the three-dot menu (⋯) in the top-right corner.
- Select Import from File.
- Choose the file
My Candleweb Trading Bot Workflow.jsonfrom your computer. - The workflow nodes and their connections should appear automatically.
- Click Save.
Step 2: Configure the imported nodes
Your workflow contains these nodes:
| Node | What it does |
|---|---|
| Schedule Trigger | Runs the workflow every 3 minutes. |
| Get the candles | Pulls the latest candles from Candleweb. |
| Has Data? → Edit Fields | Stops the run when there are no candles, and sets the memory session. |
| Prepare for AI | Builds the AI's view of the market and leaves out every id. |
| AI Agent | OpenRouter Chat Model + Simple Memory + Structured Output Parser decide BUY, SELL or HOLD. |
| Send Decision | Posts the decision back to Candleweb. |
Configure OpenRouter:
- Click the OpenRouter Chat Model node.
- Under Credentials, select an existing OpenRouter credential or create a new one.
- Get your API key from OpenRouter API Keys.
- Enter the API key and save the credential.
- Verify that the selected AI model is available to your account.
Configure Candleweb:
- Open the Get the candles node and replace the URL with the Pull URL from your trade's Connect your bot panel.
- Open the Send Decision node and replace the URL with your trade's Decision URL.
- In both nodes, replace the example value of the
X-Candleweb-Keyheader with your own developer key.
Step 3: Test the workflow
- Keep the workflow unpublished while testing.
- Click Execute Workflow or Test Workflow.
- Check that Get the candles successfully retrieves market data.
- Check that Prepare for AI shows prices and indicators, and no
id,hash,market_data_ids,delivery_idorposition_id. - Verify that the AI Agent produces a valid BUY, SELL or HOLD response.
- Check Send Decision for a successful API response.
Step 4: Enable automatic execution
Once testing succeeds, click Publish in the workflow editor. On current n8n versions, publishing enables scheduled production executions.
The imported schedule runs every 3 minutes. You can change this interval in the Schedule Trigger node; matching it to your trade's timeframe avoids paying for repeat AI calls on the same candle (see Run once per candle).
Part 2 · Build the workflow in n8n
Step 5: Save your developer key as a credential
This keeps the key out of your workflow, so it never appears in screenshots or exports.
- In n8n, go to Credentials and choose Add credential.
- Search for Header Auth and select it.
- Set Name to
X-Candleweb-Key, spelled exactly like this, with the same capital letters and dashes. Set Value to your developer key (cw_dev_...). - Rename the credential to something clear, such as Candleweb - My Bot, then Save.
Step 6: Run once per candle
- Create a new workflow. Open its Settings and set Timezone to UTC, because candles close on UTC times.
- Add a Schedule Trigger node.
- Set Trigger Interval to Custom (Cron) and enter the expression for your trade's timeframe:
| Timeframe | Cron expression | Runs |
|---|---|---|
| 1m | 15 * * * * * | every minute, at second 15 |
| 5m | 15 */5 * * * * | every 5 minutes |
| 15m | 15 */15 * * * * | every 15 minutes |
| 1h | 15 0 * * * * | every hour |
| 4h | 15 0 */4 * * * | every 4 hours |
Step 7: Get the candles
Add an HTTP Request node after the trigger and rename it Get Candles. Later steps refer to it by this exact name.
| Setting | Value |
|---|---|
| Method | GET |
| URL | your Pull URL |
| Authentication | Generic Credential Type |
| Generic Auth Type | Header Auth |
| Header Auth | Candleweb - My Bot |
| Options > Timeout | 30000 (30 seconds) |
Click Execute step. You should see something like this (shortened):
{
"status": "success",
"trade_id": 42,
"pair": "BTCUSDT",
"timeframe": "15m",
"is_stale": false,
"delivery_id": 5120,
"market_data_ids": [8801, 8802, 8803],
"position": null,
"risk_preview": {
"min_confidence": 50,
"accepted_risks": ["LOW", "MEDIUM"],
"would_arm": true,
"blocked_by": null,
"agent_exits": false,
"stop_pct": "0.0075",
"size_quote": "50.00",
"loss_at_stop": "0.63"
},
"last_exit": null,
"candles": [
{ "open_time": "2026-11-01T10:00:00Z", "open": 69210.5, "high": 69388.0,
"low": 69180.2, "close": 69355.9, "volume": 118.42, "is_closed": true,
"source": "minutes", "minutes": 15,
"indicators": { "mom_rsi_14": 61.4 },
"readiness": { ... },
"market_data_ids": [8801, 8802, 8803, 8804, 8805, ...] }
]
}trade_id,delivery_idandmarket_data_idsare the three values Candleweb needs back. Step 11 copies them across.positionis your open position on this market, with its entry price, stop and unrealised profit or loss. It isnullwhen nothing is open.risk_previewsays what a BUY right now would do: the stop distance (stop_pctis a fraction, so 0.0075 means 0.75%), the size, the loss if the stop is hit, and whether it would be accepted (would_arm; if not,blocked_bysays why).last_exitsays how the last position closed.- The response also contains
order_bookandcontext(market depth, BTC, funding and more). This guide does not pass them to the AI, but you can.
market_data_ids: 15 on a 15m candle, 60 on a 1h candle, 240 on a 4h candle. On a 1m trade each candle also has an id and a hash. position and last_exit carry a position_id. Handed the whole response, an AI ends up reading thousands of id numbers instead of prices, and its decisions are about the ids. Step 9 picks out only what the AI needs.Step 8: Skip the run when there is nothing to decide on
- Add an If node after Get Candles and rename it Has Data?
- Add the condition Array > is not empty, with the value set to the expression
{{ $json.candles }}. - Connect only the true output to the next step.
Step 9: Prepare what the AI sees
This step builds the AI's view of the market: prices, volumes, indicators, your position and what a BUY would do. It leaves out every id in the response. The ids still reach Candleweb: Step 11 copies them straight from Get Candles.
- Add a Code node after the true output of Has Data? and rename it Prepare for AI.
- Set Mode to Run Once for Each Item and Language to JavaScript.
- Replace the code in the box with:
// What the AI sees: the market and your position, and no ids.
// Send Decision copies the ids Candleweb needs straight from Get Candles.
const data = $('Get Candles').item.json;
const withoutId = (block) => {
if (!block) return null;
const { position_id, ...rest } = block;
return rest;
};
return {
json: {
market: data.pair,
timeframe: data.timeframe,
stale: data.is_stale,
position: withoutId(data.position),
risk_preview: data.risk_preview,
last_exit: withoutId(data.last_exit),
candles: (data.candles || []).map((c) => ({
time: c.open_time,
open: c.open,
high: c.high,
low: c.low,
close: c.close,
volume: c.volume,
closed: c.is_closed,
indicators: c.indicators,
})),
},
};Click Execute step. The output should show your candles' prices and indicators, and no id, hash, market_data_ids, delivery_id or position_id anywhere.
Step 10: The AI decision
- Add an AI Agent node after Prepare for AI and rename it AI Decision.
- Attach a Chat Model (for example Anthropic or OpenAI) to its Chat Model slot.
- Turn on Require Specific Output Format. This adds an Output Parser slot.
- Attach a Structured Output Parser to that slot. Set Schema Type to Define using JSON Schema, and paste the output schema.
- Set Source for Prompt to Define below, switch the Prompt field to Expression, and paste:
Market: {{ $json.market }} on the {{ $json.timeframe }} timeframe.
The data is {{ $json.stale ? 'STALE (delayed) - be cautious' : 'fresh' }}.
Open position on this market (null means none):
{{ JSON.stringify($json.position) }}
What a BUY right now would do (stop, size, and whether it would be accepted):
{{ JSON.stringify($json.risk_preview) }}
How the last position closed (null means none yet):
{{ JSON.stringify($json.last_exit) }}
Candles, oldest first. The last one may still be forming (closed false):
{{ JSON.stringify($json.candles) }}
Decide what to do now.$('Get Candles') or its whole output to the AI: both hand it the ids.Under Options, add a System Message and paste:
You are a careful crypto trading assistant for a long-only spot trade. You receive recent candles with indicators, the open position (if any), and what a BUY would do right now. Give ONE decision. What your decision does: - BUY opens a position at the market, but only if your confidence is above risk_preview.min_confidence and your risk is LOW or MEDIUM. Candleweb sets the stop and the size. A BUY does nothing while a position is open, or when risk_preview.would_arm is false (risk_preview.blocked_by says why). - SELL closes the open position at the market, but only if risk_preview.agent_exits is true and your confidence is above risk_preview.min_confidence. It never opens a short. With no open position, SELL does nothing. - HOLD does nothing. If the picture is unclear, choose HOLD. HOLD is always valid. Rules: - confidence: a whole number from 0 to 100 for how sure you are. Be honest: it decides whether Candleweb acts. - risk: LOW, MEDIUM or HIGH for how risky acting on this decision is right now. HIGH never opens a position. - Before a BUY, compare the move you expect with risk_preview.stop_pct (the stop distance as a fraction of the price) and the fees. If the move you expect is smaller than the stop, choose HOLD. - If risk_preview is null, do not BUY. - rationale: one or two short sentences, citing the specific numbers you used (for example RSI or price levels). - Use only the data given. Never invent prices or indicator values. - If the data is marked STALE, choose HOLD and say why. - Reply only in the required format.
Step 11: Send the decision back
Add an HTTP Request node after AI Decision and rename it Send Decision.
| Setting | Value |
|---|---|
| Method | POST |
| URL | your Decision URL |
| Authentication | Generic Credential Type > Header Auth > Candleweb - My Bot |
| Send Body | On |
| Body Content Type | JSON |
| Specify Body | Using JSON |
| Settings > Retry On Fail | Off is fine. If you turn it on, a retry is safe: Candleweb accepts one decision per pull and returns the first one again. |
Switch the JSON field to Expression mode and paste:
{{ JSON.stringify({
trade_id: $('Get Candles').item.json.trade_id,
delivery_id: $('Get Candles').item.json.delivery_id,
market_data_ids: $('Get Candles').item.json.market_data_ids,
decision: $json.output.decision,
confidence: $json.output.confidence,
risk: $json.output.risk,
rationale: $json.output.rationale
}) }}$json.output), and the output schema only allows valid values for those.Step 12: Test on Demo, then switch it on
Click Execute workflow. Send Decision should show a response like this (shortened):
{
"status": "success",
"repeated": false,
"message": "Decision recorded",
"decision_id": 904,
"decision": "HOLD",
"confidence": 62,
"risk": "LOW",
"entry_outcome": "NOT_AN_ENTRY",
"entry_outcome_detail": "A HOLD decision does not open a position.",
"risk_plan": null,
"entry": null,
"exit": null
}Read entry_outcome and entry_outcome_detail to see what Candleweb did:
| entry_outcome | Meaning |
|---|---|
| NOT_AN_ENTRY | A HOLD, or a SELL while SELL-to-close is off. Nothing was traded. |
| BELOW_THRESHOLD | The confidence was not above the trade's minimum, or the risk was HIGH. Normal: the bar did its job. |
| REFUSED | Candleweb would not act, for example because a position is already open or the balance is too low. The detail says why. |
| ARMED | The BUY was accepted and the entry is being placed. entry shows the fill once it happens. |
| EXIT_QUEUED | The SELL was accepted and the position is being closed. exit shows the sale once it fills. |
Toggle the workflow to Active. Watch the trade's decisions, positions and notifications in Candleweb for a while before you create a Live trade with the same bot.
Reference
The output schema
Paste this into the Structured Output Parser in Step 10. It allows exactly four fields, and only valid values for each.
{
"type": "object",
"additionalProperties": false,
"required": ["decision", "confidence", "risk", "rationale"],
"properties": {
"decision": {
"type": "string",
"enum": ["BUY", "SELL", "HOLD"],
"description": "What to do now. HOLD when unclear."
},
"confidence": {
"type": "integer",
"minimum": 0,
"maximum": 100,
"description": "How sure you are, 0 to 100."
},
"risk": {
"type": "string",
"enum": ["LOW", "MEDIUM", "HIGH"],
"description": "How risky acting on this decision is right now."
},
"rationale": {
"type": "string",
"minLength": 1,
"maxLength": 500,
"description": "One or two sentences citing the numbers used."
}
}
}The schema deliberately leaves out trade_id, delivery_id and market_data_ids. The AI never writes them, so it cannot get them wrong.
What Candleweb expects in each decision
| Field | Required | Where it comes from |
|---|---|---|
| decision | Yes | AI: BUY, SELL or HOLD |
| confidence | Yes | AI: whole number, 0 to 100 |
| risk | Yes | AI: LOW, MEDIUM or HIGH |
| market_data_ids | Yes | Copied from Get Candles |
| trade_id | Recommended | Copied from Get Candles (must match the trade in the URL) |
| delivery_id | Recommended | Copied from Get Candles (ties the decision to that pull) |
| rationale | No | AI: a short reason, shown to you. Up to 4000 characters; longer is cut. |
| entry_price | No | Not used in this guide. A price for a BUY to wait for, instead of buying at the market. |
If something goes wrong
| You see | It means | Fix |
|---|---|---|
401 X-Candleweb-Key header is required. | The credential is not attached | Choose Candleweb - My Bot under Header Auth on the node. |
401 Invalid developer key. | Wrong or old key | Re-copy the key into the Candleweb - My Bot credential. |
401 This bot is paused. | The bot is paused in Candleweb | Resume it on your bot's page. |
403 This bot is not assigned to this trade. | The key belongs to another bot | Use the key of the bot the trade points to, or change the trade's bot. |
404 Not found. | Wrong trade number in the URL | Copy the URLs again from the trade's Connect your bot panel. |
400 Timeframe '1d' is not available on the candle feed yet. | The trade is on 1 day | Use 1m, 5m, 15m, 1h or 4h. |
400 risk: Required… or confidence: … | The AI's answer is missing a field | Check Require Specific Output Format is on and the schema is pasted. |
400 on market_data_ids or trade_id | The values were typed or edited by hand | Use the Step 11 expression exactly. The values must come from Get Candles. |
| The AI's reasons talk about ids, hashes or delivery numbers instead of prices | The AI is being given Candleweb's whole response | Add the Prepare for AI step (Step 9) and use the Step 10 prompt exactly. |
200 "repeated": true | A decision was already sent for this pull | Normal if a run was repeated. The next pull gets a new decision. |
429 | Too many requests | Use the Step 6 schedule. The limits are 120 pulls and 60 decisions a minute. |
| A BUY was recorded, but nothing was bought | It did not clear the bar, or was refused | Read entry_outcome_detail on the decision. |
| A SELL did not close the position | SELL-to-close is off | Turn on Let my bot's SELL close the position on the trade. |
Menu names can differ slightly between n8n versions, and between versions of the Candleweb app. If a setting is not exactly where this guide says, look for the closest match.