Every morning someone spent 30 minutes opening a sheet, tallying the numbers, comparing to the day before, and writing a comment. Whenever the person changed, the format and the baselines wobbled with them.
The data was already in the database. What was missing was a pair of hands to put it into the same shape every day. We replaced those hands with a workflow. Two aggregation queries run against Postgres, GPT turns the results into a 3–4 sentence Korean summary, and it posts to #daily-sales automatically.
How it works
- A
Schedule Triggernode fires on a cron at 6:00 AM KST every day. - Two Postgres queries — one aggregates yesterday's sales (order count, revenue, unique customers, top channel); the other aggregates the day before yesterday's sales (for day-over-day deltas).
- A
Codenode merges the two results, formats numbers with won-unit commas (e.g.1,666,500원) and % deltas, and builds the full messages array to send to the OpenAI API. - An
HTTP Requestnode POSTs tohttps://api.openai.com/v1/chat/completions— reusing n8n'sopenAiApicredential, but calling via HTTP Request rather than the OpenAI node. Why is explained in the 'Tech notes' section below. - A
Slack Send Messagenode posts the AI summary together with the raw numbers to the#daily-saleschannel.
What you need
- n8n 2.29.9 or later (self-hosted or Cloud). Verified on self-hosted Docker.
- A Postgres database — an
orderstable with columnsid, order_no, customer_id, total (numeric), channel (text), order_date (date). If your schema differs, only the two SQL queries need editing. - An OpenAI API key — from
platform.openai.com, with a billing method attached (without one, the API just returns theWelcome to the OpenAI API!help text). Register in n8n Credentials asOpenAI account. - Slack Bot User OAuth Token —
chat:write,channels:readscopes. Starts withxoxb-. Register in n8n Credentials asSlack API. - A Slack channel to receive the report.
/invite @your-botrequired. - GPT cost: under ₩100/month at one run per day (
gpt-4o-mini, prompt context around 300 tokens).
Sample SQL schema
CREATE TABLE orders (
id bigserial PRIMARY KEY,
order_no text NOT NULL,
customer_id bigint NOT NULL,
total numeric(12,2) NOT NULL,
channel text NOT NULL, -- 'web' | 'naver-store' | 'coupang' | 'kakao-channel' | ...
order_date date NOT NULL,
created_at timestamptz NOT NULL DEFAULT now()
);Example of the actual Slack message
📈 *어제 매출 리포트 · 2026-07-14*
전일 매출 총액은 1,666,500원으로, 전전일 대비 84.1% 증가하였습니다. 주문 건수는 10건으로 66.7% 증가하였으며, 이 중 웹 채널에서 가장 높은 매출을 기록하였습니다. 특히 고객 수가 7명으로 늘어나 재구매가 활발히 이루어진 것으로 보입니다.
_원본 수치_ · 주문 10 · 매출 1,666,500원 · 평균주문가 166,650원 · 고유고객 7명 · 매출 +84.1%Tech notes — why HTTP Request instead of n8n's OpenAI node
- As of 2026-07, n8n's OpenAI node (
typeVersion 1.7) has an issue where, under certain conditions, its request shape triggers theWelcome to the OpenAI API!help text from the OpenAI API — instead of an actual completion, you get the help string. - Attaching the same
openAiApicredential to an HTTP Request node viaAuthentication → Predefined Credential Type → OpenAI APIreturns a normal completion. The credential is identical; the payload is explicit, which also makes debugging easier. - We reuse this pattern in the other AI cases (
cs-triage,meeting-notes) as well. If n8n's OpenAI node stabilizes, we can swap back — but today HTTP Request wins on production reliability.
Edge-case handling
- Zero orders yesterday — both Postgres queries wrap
totalinCOALESCE(sum(total), 0), so no NULL propagates. The AI produces a "revenue ₩0" summary and it posts to Slack normally (letting you see the fact that there were no sales at all). - OpenAI API errors — by default, HTTP Request fails the workflow on error. Immediately visible in the n8n Executions tab. In production, enable the node's
Retry on Failoption to absorb transient rate limits. - Slack bot not invited to the channel — the Slack API returns
not_in_channeland the workflow errors out. Confirm the invite before enabling. - Time zone — the cron uses the n8n instance's system time zone. Set
TZ=Asia/Seoulon the n8n container to make the 06:00 KST schedule fire correctly.
The boss complimented me on the morning report. I didn't actually write it.— Operations associate