# How to collect receipts and employee expense reports in chat

> A Pachca use case. An employee sends a receipt to the "Expenses" bot, the expense card arrives in the "Team expenses" chat, and the accountant approves it with a button. At month end, the bot compiles the expense reports and sends them for signature.

> This is the Markdown version of https://pachca.com/en/scenarios/avansovye-otchety for AI agents. The API methods named here are documented at https://dev.pachca.com/llms-en.txt — open it before calling the API. Other use cases are listed at https://pachca.com/en/llms.txt.

## What you'll get

1. The employee sends the bot `/start`. The bot replies with an "Add expense" button and opens a form with a receipt.
2. The expense card arrives in the "Team expenses" chat with "Approve" and "Clarify" buttons.
3. The bot messages the employee directly that the expense is approved and which paycheck the reimbursement comes with.
4. On the last business day of the month, the handler groups approved expenses by employee into reports from a docx template and drops the files in the thread.

The form, the card and the reports come from the bot's event handler. There are three options:

- A. No handler: receipts posted in the chat from a template. No form or buttons.
- B. No code with n8n.
- C. Your own code: the form, reports, sending to КЭДО, a payment register in 1С.

## What you need before you start

- Pachca access as an administrator or workspace owner with the scopes `chats:create`, `chat_members:write`, `group_tags:read`, `group_tags:write`, `bots:write`, `messages:create`, `pins:write`. The setup and check steps also need `search:users`, `messages:read`. Signing in is covered in the rules below.
- For option B: n8n with the Pachca node: https://pachca.com/en/help-center/integrations/avtomatizacii-v-pachke-s-pomoshchyu-n8n
- A docx expense report template approved by the accountant.

## If you're an AI agent: working rules

### Who does what

- You do everything the instructions name an API method for. Don't ask the user to create a chat, a bot or a message by hand.
- Ask the user only to sign in to Pachca and to do what is listed in "Ask the user to do by hand".
- If you have no terminal, say so right away and don't pretend you set it up. Walk the user through the steps for people on the use case page and help with the template or the handler code.

### How to talk to the user

- The user is not a developer. Write briefly and in plain words. Don't mention API methods, scopes, response codes, `id`s or CLI profiles. Say "permissions" instead of "scopes" and "the program behind the bot" instead of "handler".
- Don't report your checks. A message contains only three things: what is done, what you need from the user, what you'll do next.
- Don't ask for what you can find out through the API or what is easy to change later. Take the value from the instructions and name it.
- Take the bot's message texts from the instructions or write them yourself and carry on. Show them in your final message and offer to change them. Don't wait for approval.
- Ask the questions from "Ask the user" in the same words.

### Your first message to the user

It has three parts. The first two together take at most five lines.

1. What the user will get: one or two lines.
2. What you will create yourself: the chat, the bot and the messages, with the names from the instructions. Word anything that depends on the user's choice as a condition: "if you choose the bot, I'll also create…".
3. "What I need from you": signing in to Pachca, if the user isn't signed in yet, and the questions from "Ask the user". If the use case or one of its options needs n8n, ask here whether the user has it and ask for access right away in the words from the "n8n" section below: "if you have n8n, send me…".

If part of the result appears only after a manual step by the user, say so in one line: "you'll schedule the retro questions yourself, I'll show you how".

The user's answers are their consent to the plan. Start the setup right after them, without asking for a second confirmation.

### Access to Pachca

- The user doesn't need to issue a token or send it in the chat. Before your first message, check the login: `npx -y @pachca/cli@latest auth status`. If there is none, ask the user to run `npx -y @pachca/cli@latest auth login` in their own terminal. The CLI shows a code; the user enters it in the browser and confirms access. Write the commands with `@latest`: without it an old cached CLI version may start, and it asks to paste a token instead of signing in through the browser.
- This login has the same rights as the user's role. Compare them with the scopes in "What you need before you start" yourself: `npx -y @pachca/cli@latest api GET /oauth/token/info` shows the scopes. If something is missing, say in plain words what you won't be able to do, for example "I can't send messages". There are two ways out: add this permission to the token in Pachca, under "Automations" → "API", or sign in again as the workspace owner or an administrator.
- If `auth status` shows `scopes: null`, it doesn't mean there are no rights: look at `GET /oauth/token/info`. The checks in these rules also need `search:chats`, `bots:read` and `profile:read`.
- When the user says "me", they mean the owner of the login. `GET /profile` returns their `id` and nickname.
- Call the API with `npx -y @pachca/cli@latest api <METHOD> <path> -o json`. Write the full command in every call; don't put it into a shell variable.
- Pass query parameters as `--query key=value`, for example `--query query=Purchasing`. The CLI silently drops a parameter without a key and returns the whole list, so it's easy to conclude that the chat already exists.
- Pass the request body with `--data` or `--input`. Almost every write method wraps the body in an object: `chat` for `POST /chats`, `message` for `POST /messages`, `task` for `POST /tasks`, `group_tag` for `POST /group_tags`, `user` for `PUT /users/{id}`, `webhook` for bots. `--describe` shows the wrapper; write its path with `{id}`, not with a number. Without the wrapper, `POST /chats` creates the chat but silently adds no members.
- Don't print tokens or bots' incoming webhook URLs in the chat. A bot's token is shown only once, when it's created: save it to a file.

### n8n

- If the user has no n8n, say that it comes as the hosted n8n Cloud or can be installed on their own server, and give the link for installing the Pachca nodes: https://dev.pachca.com/guides/n8n/setup.md. These instructions don't cover installing n8n. If they have an option without n8n, suggest starting with it and coming back to the bot once n8n is there.
- If the user has n8n, ask for access in these words: "Send me your n8n address: the link you open it with in the browser. Don't send the key in the chat. In n8n, open Settings → n8n API → Create an API key and copy the key. Right after that, run the command below in your terminal exactly as it is; you don't need to put anything into it. It saves the copied key to a file and prints nothing. Then write 'key saved'." The command for macOS: `pbpaste > ~/n8n-key.txt`. For another system, pick the matching one.
- Build everything in n8n on the official Pachca nodes, `n8n-nodes-pachca`. Don't use the `HTTP Request` node for Pachca API calls.

### Setup

- Take API methods from https://dev.pachca.com/llms-en.txt, don't rely on memory. The command `npx -y @pachca/cli@latest api <METHOD> <path> --describe` shows a method's schema.
- The Pachca skills cover the call order and error handling for chats, bots and forms: `npx skills add pachca/openapi`. Install them when you need them, without asking the user.
- Before creating anything, check whether the chat and the bot already exist: `GET /search/chats`, `GET /bots`. If they do, ask whether to set them up or create new ones.
- `POST /group_tags` creates an empty tag. `PUT /users/{id}` with `list_tags` assigns it to an employee; it needs the `users:update` scope. The field replaces the whole set of tags: first read the employee's current tags with `GET /users/{id}` and send them together with the new one. While the tag has no members, mentioning it notifies no one.
- If a method returns an error, stop and tell the user in plain words what failed and what you need to continue.
- The messages you post yourself and the ones the bot sends must be easy to read on a screen, without overdoing spacing or emphasis. Make the heading bold and start the text on the next line. Write connected text as one paragraph. Put rules and steps on numbered lines one under another and separate them from the intro with a single empty line. A short message needs no other empty lines. In a card, make the field labels bold and put the fields on consecutive lines. Use bold only for the heading and the field labels. Pachca markup has no lists or tables: the numbers at the start of lines stay plain text. A sample:

  ```text
  **Retro rules**
  On the last day of the sprint a message with questions arrives here. We reply in the thread under it.

  1. Each point is a separate message.
  2. We add 👍 to what is worth discussing.
  3. The facilitator posts the outcomes in one message and pins it.
  ```
- A pin can't be checked through the API: only a successful `POST /messages/{id}/pin` response confirms it.
- If you sent service messages to a chat to check a bot, such as `/start`, delete them: `DELETE /messages/{id}`. Don't delete messages with rules and templates.
- At the end, say what was created, what is left for the user to do by hand and how to check that the use case works.
- To undo the setup: `DELETE /bots/{id}` deletes the bot, `PUT /chats/{id}/archive` sends the chat to the archive.

## How to set up the use case with the user

### 1. Ask the user

Ask these questions in these words:

1. How will employees submit receipts? With a message that follows a template in the chat: it works right away. Or with a form from the «Расходы» bot, which also builds the monthly reports: that needs n8n. Do you have n8n? If not, or if you're not sure, we'll start with the simple option and add the bot later.
2. Who is the accountant who accepts receipts? Send me the name.
3. Which managers should see the expenses? I'll add them to the «Расходы сотрудников» chat.
4. By when and in what form do employees submit receipts? This goes into the chat rules.

Check yourself whether the workspace has a «Бухгалтерия» tag. The bot needs a docx expense report template: ask for it when you get to the handler.

### 2. Do it yourself through the Pachca API: the chat and the tag

These steps are needed in every option.

1. Find the `id` of the accountant and the managers: `GET /search/users` or `GET /users`.
2. Find the "Accounting" tag: `GET /group_tags` with `names`.
3. If the tag doesn't exist, create an "Accounting" tag with the people who approve receipts: `POST /group_tags`. Check the method's schema with `--describe`. An @Accounting mention in an expense thread reaches all of them.
4. Create the chat: `POST /chats` with `name: "Расходы сотрудников"`, `member_ids` and the tag's `id` in `group_tag_ids`. Save the chat's `id` for the handler. If the chat already exists, add the tag to it: `POST /chats/{id}/group_tags` with `group_tag_ids`.

### 3. Option A: receipts from a template

1. Post the receipt template: `POST /messages` to the chat with this text:

```
Чек: фото
Что оплатили: такси до клиента, встреча с «Северным ветром»
Сумма: 1 240 ₽
Дата: 24.09.2026
```

2. Pin the template: `POST /messages/{id}/pin`.
3. Offer to post the rules: `POST /messages`. Receipts go in on the day of purchase. The accountant reacts with ✅ when a receipt is approved and asks questions in the thread with an @mention. Once a month, the accountant compiles the expense reports by hand.
4. Ask the user whether to create the "Expenses" bot now. If yes, do steps 1-2 of section 4.

### 4. Options B and C: do it yourself through the Pachca API

1. Create the bot: `POST /bots` with `name: "Расходы"`, a nickname ending in `_bot`, `trigger_on: "commands"`, `commands: ["/start"]` and `events: ["message_new", "button_click"]`. Don't set `single_chat: true`: the bot works in multiple chats. From the response, save the bot's `id` and `access_token`: the token is shown once, and the handler needs it.
2. Add the bot to the chat: `POST /chats/{id}/members` with the bot's `id` in `member_ids`.
3. Once the handler has an address, set it as the outgoing webhook: `PUT /bots/{id}` with `outgoing_url` using a personal token, or `PUT /bot/webhook` using the bot's token. For n8n, this is the Production URL of the first workflow.
4. Offer to post the rules from the section "Rules for the team": `POST /messages`.

### 5. Handler: specification

- Events: the `/start` command and button clicks.
- On `/start`, the bot replies with an "Add expense" button. The button opens the form through `POST /views/open` with the `trigger_id` from the click event.
- Form fields: "What was paid for", "Amount", "Date", "Category", "Receipt". A `file_input` block works for the receipt.
- The card in the "Team expenses" chat: the amount, the date and the receipt photo, with "Approve" and "Clarify" buttons. The receipt is uploaded through the Pachca files API: https://dev.pachca.com/api/file-uploads.md
- "Approve": the expense is written to a table, and the bot messages the employee directly that the expense is approved and which paycheck the reimbursement comes with.
- "Clarify": the accountant asks the employee a question in the card's thread.
- Report: a second scheduled process on the last business day of the month groups approved expenses by employee, fills in the docx template and drops the files in the thread.

Option B: use the workflow of the time-off bot as a model: https://pachca.com/en/blog/avtomatizaciya-pachka-n8n-zayavki-na-otpusk. Swap the form fields and buttons for the ones listed above. The second workflow runs on a Schedule Trigger.

Option C: a developer or an AI assistant writes the handler from this specification. Sending reports to КЭДО and a payment register in 1С get added there too.

### The handler in n8n

Build the handler on version 2 of the Pachca nodes, `n8n-nodes-pachca`. They must be installed in the user's n8n: https://dev.pachca.com/guides/n8n/setup.md

A complete working workflow with a form, buttons and thread replies: https://pachca.com/scenarios-agent/propusk-bot-n8n.json. Its texts are in Russian. Use it as the base and replace the form fields, buttons, texts and branches for your use case. The file has placeholders: `CHAT_ID`, `MANAGERS`, `CREDENTIAL_ID` and `NEW-UUID`. Take node parameter names from it and from the list below. Some examples on dev.pachca.com use different names, and a workflow built from them won't work.

What to build from:

- `Pachca Trigger` receives the bot's events. Use one per workflow: a bot has a single webhook address. The `event` parameter takes one value; set it to `*`, which means all events. Set `webhookSetup` to `manual`: you will set the bot's address yourself. The node needs its own `webhookId`, a new UUID.
- A `Code` node after the trigger parses the event and picks a branch. A new message: `type: "message"`, `event: "new"`, the text is in `content`. A button press: `type: "button"`, the button's value is in `data`, with `trigger_id` and `message_id` next to it. A form submission: `type: "view"`, `event: "submit"`, the form's `callback_id`, field values in `data` by field name. `user_id` shows who acted. Event fields: https://dev.pachca.com/guides/webhook/events.md
- `Switch` routes events by the branch from the `Code` node.
- Form: the `Pachca` node, `resource: "form"`, `operation: "create"`. The title goes into `formTitle`; `triggerId` comes from the button press event and is valid for three seconds. `formBuilderMode: "json"`, the fields go into `formBlocks` as a JSON string. `callbackId`, `submitText` and `closeText` sit inside `additionalFields`. Block types: https://dev.pachca.com/guides/forms/blocks.md
- Message: `resource: "message"`, `operation: "create"`, `entityType` and `entityId`, the text in `content`. Post to a chat with `entityType: "discussion"`; message an employee directly with `entityType: "user"` and their `user_id` in `entityId`. In the sample, the button after `/start` goes to the chat: if the bot should reply in direct messages in your use case, change this. Buttons: `buttonLayout: "raw_json"` and `rawJsonButtons` as a JSON string. It is an array of rows, each row an array of buttons with `text` and `data`. Mention an employee as `<@user_id>`.
- A reply in the thread under a message. First `resource: "thread"`, `operation: "create"` with `threadMessageId`: the `message_id` of the message that needs a thread. The node returns the thread's `id`. Then a message with `entityType: "thread"` and that `id` in `entityId`.
- A button press inside a thread brings the thread's `chat_id`, but a reply needs the thread's `id`. Put the thread's `id` into the `data` of the buttons you post in the thread.
- If not everyone may press a button, compare `user_id` from the event with the list in the handler spec and send nothing on someone else's press.

- Chat members: `resource: "member"`, `operation: "getAll"`, the chat in the `id` parameter, `returnAll: true`, `simplify: false`. The bot itself is in this list with the `bot` flag: filter it out.
- A schedule is started by a `Schedule Trigger` node with a `cronExpression` rule.

The sample stores nothing: everything a reply needs is in the buttons' `data`, namely the action and the author's `id`. If the use case has to remember a little data, such as a request number counter or who has already answered, use the workflow's static data: `$getWorkflowStaticData('global')` in a `Code` node. It is saved only on runs of an active workflow, not on manual runs in the editor. Name this decision to the user in your final message. Keep meeting room schedules, registers and anything that must survive recreating the workflow in an external table: choose it together with the user.

Steps:

1. Build the workflow file. Keep only `name`, `nodes`, `connections` and `settings`: the n8n API rejects other fields. Check that the JSON is valid and that node names in `connections` and in expressions match the names in `nodes`.
2. Get the n8n address and API key as described in the rules above. Requests to the n8n API carry the `X-N8N-API-KEY` header.
3. Create a Pachca credential in n8n: `POST /api/v1/credentials` with `type: "pachcaApi"`. In `data`, put `accessToken` with the bot token and `baseUrl` with `https://api.pachca.com/api/shared/v1`. `GET /api/v1/credentials/schema/pachcaApi` shows the other required fields. If `allowedHttpRequestDomains` is among them, set it to `all`. Put the `id` and name of this credential into every Pachca node in the file.
4. Upload the workflow: `POST /api/v1/workflows`. Activate it: `POST /api/v1/workflows/{id}/activate`.
5. Set the bot's webhook address: `PUT /bots/{id}` with `outgoing_url`. The address is the n8n address, then `/webhook/`, the `webhookId` of the `Pachca Trigger` node and `/webhook`.
6. To check a scheduled run without waiting, temporarily set the schedule to the next minute, wait for the run and set it back.
7. If there is no n8n API key or a step fails, give the file to the user. They paste its contents into a new workflow, create a credential with the bot token, select it in every Pachca node and activate the workflow. Ask for the Production URL of the `Pachca Trigger` node and set it for the bot yourself.

### 6. Ask the user to do by hand

- Make the "Expenses" bot public: turn on the "Public" toggle in the bot's settings. This can't be set through the API.
- If creating the tag through the API didn't work: in the chat, open Members → table view → the tag icon and create an "Accounting" tag with the people who approve receipts.
- Import the workflows into n8n and turn them on, if you don't have access to n8n.

### 7. Check

1. Ask the user to send the bot `/start`. The bot should reply with an "Add expense" button.
2. Ask them to fill in the form and submit it. Make sure the card appeared in the chat: `GET /messages` for the "Team expenses" chat.
3. Ask them to click "Approve". The user confirms the bot's reply in the employee's direct messages: your token can't see a private conversation with the bot.

## Rules for the team

A receipt goes in on the day of purchase. The accountant approves or asks a question within the day, with questions going in the thread via an @mention. Anything not approved by the 25th goes into next month's report. Deadlines and requirements come from the company's accounting policy.

## What the use case can't do

- Expense reports are signed in КЭДО. Pachca only shows the status.
- The report template and category limits are checked with the accountant before launch.

## Troubleshooting

- The bot doesn't reply to `/start`. "New messages" isn't selected in the outgoing webhook, or the handler is off. n8n needs the Production URL, the Test URL won't work.
- The button doesn't open the form, or "Approve" doesn't work. "Button clicks" isn't selected in the outgoing webhook: check `button_click` in the bot's `events`.
- The receipt isn't attached to the card. The handler passed only the file name and didn't upload the file through the files API: https://dev.pachca.com/api/file-uploads.md

## Links

- Use case page with the demo and instructions for a human: https://pachca.com/en/scenarios/avansovye-otchety
- Time-off bot workflow: https://pachca.com/en/blog/avtomatizaciya-pachka-n8n-zayavki-na-otpusk
- n8n in Pachca: https://pachca.com/en/help-center/integrations/avtomatizacii-v-pachke-s-pomoshchyu-n8n
- Forms: https://pachca.com/en/help-center/integrations/formy
- Buttons in chatbots: https://pachca.com/en/help-center/integrations/knopki-v-chat-botah
- Outgoing webhooks: https://pachca.com/en/help-center/integrations/ishodyashchie-webhook-kak-nastroit
- Tags for developers: https://dev.pachca.com/guides/tags.md
- Forms for developers: https://dev.pachca.com/guides/forms/overview.md
- Outgoing webhooks and event types: https://dev.pachca.com/guides/webhook/events.md
