# Answer Support Tickets from a Rails Codebase

Yes. Your backend sends each ticket to the API, an agent finds the rule in your Rails models, and the reply comes back as JSON on your webhook.

[Run this on Rush](https://cloud.getrush.ai/run/answer-support-tickets-from-code/rails/), in a Project that has your repository.

## What the agent does

A ticket arrives in your help desk and your backend starts one run for it, with the app's repository in `resources` and the customer's question in the prompt. The agent clones the repository into its own cloud sandbox and answers from code. In a Rails app like `lobsters/lobsters`, it sees Rails 8.1 in `Gemfile.lock`, Ruby 4.0 from `.ruby-version`, and the posting rules as validations on `Story` in `app/models/story.rb`, with the domain record in `app/models/domain.rb`.

For "why can a new account not post links to some domains?", the agent starts at what "new" means. `User#is_new?` in `app/models/user.rb` is true while `created_at` is within `NEW_USER_DAYS`, which the same file sets to 70. Then it reads the validations that call it. `check_not_new_domain_from_new_user` adds "is an unseen domain from a new user." when a new user submits a URL whose domain has no stories that are not deleted, and opens a mod mail to the user's inviter explaining the rule.

It reads the neighbouring rules, because a customer's link may fail for a different reason that looks the same. `check_not_banned_domain` refuses any user's link to a domain with `banned_at` set and quotes `banned_reason`; `check_not_banned_origin` does the same for an origin. `check_not_brigading` stops new users from linking into project bug trackers and discussions on GitHub, GitLab, Codeberg and others. `check_tags` stops new users from using tags where `permit_by_new_users` is false. Each has its own error text, so the reply can tell the customer which one they hit.

The agent writes the JSON the prompt asks for: two short paragraphs a member of the community can read, the files and lines for each rule, and its confidence. It changes no code. The answer says when the rule stops applying, 70 days after signup, and that a link to a domain someone has already posted is not refused by this rule.

## Start it with one API call

`POST https://api.prix.dev/o/{org}/sessions`

```json
{
  "agent": "claude",
  "input": "A customer asks: why can a new account not post links to some domains? Find the answer in this repository's code, not its docs: read User#is_new? and NEW_USER_DAYS in app/models/user.rb, then every Story validation in app/models/story.rb that refuses a URL, and the Domain model. Say which rule matches each error message and when it stops applying. Reply with JSON: answer (two short paragraphs a customer can read), files (paths and lines that prove it), and confidence. Do not change code.",
  "resources": [
    {
      "url": "https://github.com/your-org/your-app"
    }
  ]
}
```

1. **Get an API key.** In the console, open Settings, then API keys, and create a key for your org. Send it in the Authorization header as a Bearer token.
2. **Send the request.** POST the body in request.json to https://api.prix.dev/o/{org}/sessions, with your repository as the resources URL. The run starts in its own cloud sandbox.
3. **Read what came back.** Follow the run on its live stream or wait for the run.completed webhook, then read the result and its trajectory.

## What comes back

The run's final answer is the JSON object the prompt asks for. `answer` explains that accounts younger than 70 days cannot submit a link to a domain nobody has posted before, and how that differs from a banned domain, which no account can post. `files` cites `app/models/user.rb` for `is_new?` and `NEW_USER_DAYS`, and `app/models/story.rb` for `check_not_new_domain_from_new_user` and `check_not_banned_domain`, with lines. `confidence` reflects whether the customer's error text was in the ticket.

It reaches your backend as `data.result` on the `run.completed` webhook, and the session read returns the same `result`. Your backend parses it and attaches the answer to the ticket. Before sending, a moderator or support person matches the customer's error message to the rule the answer names, and checks the cited lines against the deployed version.

The session keeps every file the agent opened and every search it ran, so the reasoning behind each reply can be read later. The specs under `spec/models/story_spec.rb` and `spec/models/domain_spec.rb` are a natural next read if the answer is ever in doubt.

## Run it on every new support ticket

Each ticket starts one `POST /o/{org}/sessions` from your help desk's webhook handler, with the question in `input` and the ticket id as the `Idempotency-Key`. The API answers `202` at once with the `execution_id`, which you store on the ticket.

With a webhook on the API key, your backend receives the reply when the run finishes and never polls. If the customer writes back with the exact error they saw, post it to the same session as a follow-up and the agent narrows the answer to that rule.

## Why run it on Rush?

Rules like these live in model validations that only engineers read, and a moderation or support team ends up asking them each time. A Rush run starts with the Rails app cloned in its own sandbox, reads the validations for itself, and returns a reply with citations, without anyone on the team setting up Ruby 4.0 and the gems to look.

Each ticket gets its own run, and many run at once. Every reply cites the code it is based on and the session keeps the whole record, so a support person can send it with the same confidence an engineer would have, and the engineer can check it in a minute.

## Frequently asked questions

### Does the agent need to boot the Rails app?

No. This question is answered by reading `app/models/story.rb` and `app/models/user.rb`, and the prompt says not to change code. If a ticket needs a running app, the sandbox has a shell and the repository ships a `docker-compose.yaml` for development.

### What if the customer hit a different rule?

The reply lists the rules that refuse a URL with their error messages, so the customer's message picks the right one. Sending the exact error in the prompt makes the answer specific.

### Can it tell the customer when the restriction ends?

Yes, when the code says. Here `is_new?` compares `created_at` with `NEW_USER_DAYS.days.ago`, and `NEW_USER_DAYS` is 70, so the reply can say the rule stops applying 70 days after the account was created.

### Where does the JSON arrive?

In `data.result` of the `run.completed` webhook, up to 16 KiB, and in `result` on `GET /o/{org}/sessions/{id}`. Parse and validate it in your backend before it reaches a customer.
