Your keys, your exchange. Candleweb never holds your funds.

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:

  1. Gets the latest candles for your trade from Candleweb, with your open position and what a buy would do right now.
  2. Asks an AI what to do: buy, sell or hold.
  3. 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]
Your bot's decisions can place trades.
A confident BUY opens a position, and a SELL can close one if you allow it. On a Demo trade this uses your paper account: real market prices, no real money. On a Live trade, Candleweb places real orders on your exchange. Build and test on a Demo trade first.

How Candleweb acts on a decision

DecisionWhat happens
BUYCandleweb 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.
SELLCandleweb 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.
HOLDRecorded. 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:

SettingChoose
NameAnything you like, for example My n8n bot
Platformn8n Cloud or n8n self-hosted
Data deliveryMy bot will pull. Push is not available yet.
Reply timeout60 seconds. An AI usually answers in 5 to 30 seconds.
Decision styleBuy / sell / hold or Buy / hold only. Candleweb does not act on Signal strength bots.
When there is no replyLeave it on Skip the candle.

Save, then copy the bot's developer key. It looks like cw_dev_3f9a1b2c....

Keep your developer key private.
Anyone who has it can send decisions as your bot, and those decisions can trade. If you regenerate the key, the old one stops working immediately, so update n8n (Step 5) straight away.

Step 3: Create a trade and point it at your bot

Go to Trades, create a new trade, and fill in:

SettingWhat to know
Exchange accountNeeded for a Live trade. A Demo trade can run without one.
PairThe market to trade, for example BTCUSDT.
Timeframe1m, 5m, 15m, 1h or 4h. Do not choose 1 day: the candle feed does not support it yet.
Allocated amountThe money this trade may use.
IndicatorsPick them from the list. Your bot receives their values with every candle.
Candle historyHow many candles your bot receives on each pull (20 by default).
Who decidesYour bot (not CandleAI), then choose the bot from Step 2.
Minimum confidenceCandleweb 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 stopOn by default. The stop follows the price up as each candle closes.
Let my bot's SELL close the positionOff 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:

WhatMethodLooks like
Pull URLGEThttps://api.candleweb.io/api/trading_v2/trades/42/candles/
Decision URLPOSThttps://api.candleweb.io/api/trading_v2/trades/42/decision/
Developer keycw_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.

The Candleweb trading bot workflow in the n8n editor: Schedule Trigger, Get the candles, Has Data?, Edit Fields, AI Agent with OpenRouter Chat Model, Simple Memory and Structured Output Parser, then Send Decision.
My Candleweb Trading Bot Workflow.jsonn8n workflow · JSON
Download template

Step 1: Import the workflow into n8n

  1. Open your n8n dashboard (either n8n Cloud or your self-hosted n8n).
  2. Click Create Workflow or New Workflow.
  3. In the workflow editor, click the three-dot menu (⋯) in the top-right corner.
  4. Select Import from File.
  5. Choose the file My Candleweb Trading Bot Workflow.json from your computer.
  6. The workflow nodes and their connections should appear automatically.
  7. Click Save.

Step 2: Configure the imported nodes

Your workflow contains these nodes:

NodeWhat it does
Schedule TriggerRuns the workflow every 3 minutes.
Get the candlesPulls the latest candles from Candleweb.
Has Data? → Edit FieldsStops the run when there are no candles, and sets the memory session.
Prepare for AIBuilds the AI's view of the market and leaves out every id.
AI AgentOpenRouter Chat Model + Simple Memory + Structured Output Parser decide BUY, SELL or HOLD.
Send DecisionPosts the decision back to Candleweb.
Imported the template before?
Earlier copies sent the AI Candleweb's ids along with the market data. Download the template again and import it fresh. The picture above does not show the new Prepare for AI node yet, but your import will include it, between Edit Fields and AI Agent.

Configure OpenRouter:

  1. Click the OpenRouter Chat Model node.
  2. Under Credentials, select an existing OpenRouter credential or create a new one.
  3. Get your API key from OpenRouter API Keys.
  4. Enter the API key and save the credential.
  5. Verify that the selected AI model is available to your account.

Configure Candleweb:

  1. Open the Get the candles node and replace the URL with the Pull URL from your trade's Connect your bot panel.
  2. Open the Send Decision node and replace the URL with your trade's Decision URL.
  3. In both nodes, replace the example value of the X-Candleweb-Key header with your own developer key.

Step 3: Test the workflow

  1. Keep the workflow unpublished while testing.
  2. Click Execute Workflow or Test Workflow.
  3. Check that Get the candles successfully retrieves market data.
  4. Check that Prepare for AI shows prices and indicators, and no id, hash, market_data_ids, delivery_id or position_id.
  5. Verify that the AI Agent produces a valid BUY, SELL or HOLD response.
  6. Check Send Decision for a successful API response.
Test on Demo only.
Because this workflow sends trading decisions to Candleweb, test it on a Demo trade only.

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).

Final check
Open the Executions tab to monitor successful runs, API errors and AI Agent failures. If the workflow shows Unknown node type after import, check your n8n version and whether its AI/LangChain nodes are supported.

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.

  1. In n8n, go to Credentials and choose Add credential.
  2. Search for Header Auth and select it.
  3. 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_...).
  4. Rename the credential to something clear, such as Candleweb - My Bot, then Save.

Step 6: Run once per candle

  1. Create a new workflow. Open its Settings and set Timezone to UTC, because candles close on UTC times.
  2. Add a Schedule Trigger node.
  3. Set Trigger Interval to Custom (Cron) and enter the expression for your trade's timeframe:
TimeframeCron expressionRuns
1m15 * * * * *every minute, at second 15
5m15 */5 * * * *every 5 minutes
15m15 */15 * * * *every 15 minutes
1h15 0 * * * *every hour
4h15 0 */4 * * *every 4 hours
Why second 15, and why match the timeframe
A candle is saved a few seconds after it closes, so your bot always gets the fresh one. Each pull asks your bot for a decision: pulling every minute on a 1h trade would run (and pay for) sixty AI calls on the same candle.

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.

SettingValue
MethodGET
URLyour Pull URL
AuthenticationGeneric Credential Type
Generic Auth TypeHeader Auth
Header AuthCandleweb - My Bot
Options > Timeout30000 (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_id and market_data_ids are the three values Candleweb needs back. Step 11 copies them across.
  • position is your open position on this market, with its entry price, stop and unrealised profit or loss. It is null when nothing is open.
  • risk_preview says what a BUY right now would do: the stop distance (stop_pct is 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_by says why).
  • last_exit says how the last position closed.
  • The response also contains order_book and context (market depth, BTC, funding and more). This guide does not pass them to the AI, but you can.
The response is full of ids. The AI must not see them.
Each candle lists the stored rows behind it in 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

  1. Add an If node after Get Candles and rename it Has Data?
  2. Add the condition Array > is not empty, with the value set to the expression {{ $json.candles }}.
  3. 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.

  1. Add a Code node after the true output of Has Data? and rename it Prepare for AI.
  2. Set Mode to Run Once for Each Item and Language to JavaScript.
  3. 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

  1. Add an AI Agent node after Prepare for AI and rename it AI Decision.
  2. Attach a Chat Model (for example Anthropic or OpenAI) to its Chat Model slot.
  3. Turn on Require Specific Output Format. This adds an Output Parser slot.
  4. Attach a Structured Output Parser to that slot. Set Schema Type to Define using JSON Schema, and paste the output schema.
  5. 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.
Use this prompt exactly.
Keep Source for Prompt set to Define below. Do not use Take from previous node automatically, and do not pass $('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.

SettingValue
MethodPOST
URLyour Decision URL
AuthenticationGeneric Credential Type > Header Auth > Candleweb - My Bot
Send BodyOn
Body Content TypeJSON
Specify BodyUsing JSON
Settings > Retry On FailOff 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
}) }}
Why this is safe
The first three lines are copied straight from Get Candles, exactly as Candleweb sent them. Only the last four come from the AI's answer ($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_outcomeMeaning
NOT_AN_ENTRYA HOLD, or a SELL while SELL-to-close is off. Nothing was traded.
BELOW_THRESHOLDThe confidence was not above the trade's minimum, or the risk was HIGH. Normal: the bar did its job.
REFUSEDCandleweb would not act, for example because a position is already open or the balance is too low. The detail says why.
ARMEDThe BUY was accepted and the entry is being placed. entry shows the fill once it happens.
EXIT_QUEUEDThe 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

FieldRequiredWhere it comes from
decisionYesAI: BUY, SELL or HOLD
confidenceYesAI: whole number, 0 to 100
riskYesAI: LOW, MEDIUM or HIGH
market_data_idsYesCopied from Get Candles
trade_idRecommendedCopied from Get Candles (must match the trade in the URL)
delivery_idRecommendedCopied from Get Candles (ties the decision to that pull)
rationaleNoAI: a short reason, shown to you. Up to 4000 characters; longer is cut.
entry_priceNoNot used in this guide. A price for a BUY to wait for, instead of buying at the market.

If something goes wrong

You seeIt meansFix
401 X-Candleweb-Key header is required.The credential is not attachedChoose Candleweb - My Bot under Header Auth on the node.
401 Invalid developer key.Wrong or old keyRe-copy the key into the Candleweb - My Bot credential.
401 This bot is paused.The bot is paused in CandlewebResume it on your bot's page.
403 This bot is not assigned to this trade.The key belongs to another botUse the key of the bot the trade points to, or change the trade's bot.
404 Not found.Wrong trade number in the URLCopy 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 dayUse 1m, 5m, 15m, 1h or 4h.
400 risk: Required… or confidence: …The AI's answer is missing a fieldCheck Require Specific Output Format is on and the schema is pasted.
400 on market_data_ids or trade_idThe values were typed or edited by handUse 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 pricesThe AI is being given Candleweb's whole responseAdd the Prepare for AI step (Step 9) and use the Step 10 prompt exactly.
200 "repeated": trueA decision was already sent for this pullNormal if a run was repeated. The next pull gets a new decision.
429Too many requestsUse the Step 6 schedule. The limits are 120 pulls and 60 decisions a minute.
A BUY was recorded, but nothing was boughtIt did not clear the bar, or was refusedRead entry_outcome_detail on the decision.
A SELL did not close the positionSELL-to-close is offTurn on Let my bot's SELL close the position on the trade.
Good to know
Your bot owes one decision per candle it is shown. A candle with no decision within the reply timeout counts as a missed reply in your bot's health stats. Sending HOLD does not. If your AI model has an outage, the run stops at AI Decision and nothing is sent.

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.