---
name: crear-reporte
description: "Create a published, refreshable Frepi report (share link, versions, in-app data refresh) from saved queries. Use when the user asks for a report, informe or reporte, or to refresh, republish or share one."
license: "Proprietary. Free to use with a Frepi account. See LICENSE."
metadata:
  author: "frepi-ai"
  version: "11"
---

# Crear un reporte

A Frepi report is one HTML snapshot plus a recipe: the saved queries it was built from. The
platform refreshes the data from that recipe, so every number must come from a saved query.

## 1. Check what exists

- `bi_list_reports`. If a report on the same topic exists, show it and offer to update it
  instead of creating a near copy: over MCP, republish with its `token`; in the copilot, the
  operator updates it from its page in Reportes.
- Read the brand and use it, without asking: `list_assets(kind="brand")` returns the resolved
  brand (workspace kit, else the host's theme, else the Frepi default or neutral), with its
  logos, colors, chart colors, fonts and voice, plus `brand.level`. How to use them is in
  `references/marca.md` (`bi_get_skill(name="crear-reporte", reference="marca")`). If the user
  asks for another look, the request wins. If `brand.level` is `frepi_default` or `neutral`,
  the workspace has no brand yet: use that brand and suggest once, in one line, that the
  operator fills it in (Recursos › Marca, or give you the colors and logo to save with
  `save_asset(kind="brand")`), then go on with the report.

## 2. Get the numbers

- Explore with `list_tables` → `describe_table` → `query` (over MCP: `bi_list_tables` →
  `bi_describe_table` → `bi_query`). Never estimate or copy a number from memory. If a query
  returns nothing, the report says so.
- Save every query the report uses with `save_query` (over MCP: `bi_save_query`). Each one
  becomes a data source: 1 to 12, each with a distinct integer `saved_query_id`, plus its params
  and a short `note`.
- A query that cannot be saved (SQL error, no permission) means no report. Say so. Never publish a
  document with fixed numbers instead.

## 3. Publish

**In the Frepi copilot** (you have `create_report`):

- Call `create_report` with these fields only:
  - `title` (required).
  - `data_sources` (required, 1 to 12): `[{"saved_query_id": 412}]`. Each entry takes an
    integer `saved_query_id` from `save_query`, and optional `params`, `note` and `bind`.
  - Optional: `subtitle`, `body_html` (body only: `.section`, `.kpi-grid`/`.kpi`,
    `<canvas id>`), `init_script` (renders everything from `kit.data("<saved_query_id>")`),
    `primary_color`, `brand_css`, `font_asset_ids`, `visibility`, `parameters`,
    `conversation_links`.
- It has no token, access, html or conclusions field: those are MCP only. Any other field is
  refused.
- The server adds the kit, the data islands and the «Datos al …» date. Do not write `<script>`,
  `<style>`, `<textarea>`, `<iframe>`, islands or `.as-of` in `body_html`. Call only the kit
  functions that exist (Rule 8).
- `brand_css` takes only custom properties, color, background-color, border-color, fill,
  font-family, font-weight and font-style (see `references/marca.md`).
- The card it shows is the approval. One `create_report` per turn: ask for a second report in
  the next turn.
- Without `body_html` the server adds one table per source and one button per conversation link.

**Over MCP** (you have `bi_publish_report`):

1. Read the full convention: `bi_get_skill(name="crear-reporte", reference="convencion")`. It
   holds the rules the publish endpoint enforces.
2. Read the kit: resources `report-kit://css`, `report-kit://js`, `report-kit://example`.
3. Compose one self-contained HTML file: kit CSS and JS inlined, one JSON island per data source
   (`<script type="application/json" class="ds" data-ds="<id>">`), every number rendered from
   `kit.data(...)`, and a plain-text `<span class="as-of">Datos al …</span>`.
4. `bi_publish_report(title, html, data_sources)`. Republish with `token` to keep the URL.
   `access`: `login` (private, default) or `public` (needs configure capability; refused on SSO
   partnerships).

**Parameters and conversation buttons** (optional, convention 4; Rules 9 and 10 of the convention):

- A control that changes the DATA (period, line, a category) is a parameter; one that only changes
  how rows already on the page are shown (sort, tabs) stays in the HTML.
- Declare `parameters` (one `period`, up to three `choice`) and map each saved query's own
  parameters to them with `data_sources[].bind`, e.g. `{"saved_query_id": 12, "bind": {"rango": "period"}}`.
  A period binds to a `date-range` parameter; a choice to `enum`, `text` or `number`.
- Declare `conversation_links` (`label` with `{count}` once, `tag_ids`, optional `line_param`,
  `period_param`) and put `<a data-conv-link="<key>">texto</a>` in the HTML: plain text, no
  `href`, no `data-open`. The platform writes the link and the count. Never write the href by hand.

**AI conclusions** (MCP only, through `bi_publish_report`; optional, convention 4; Rule 11 of the convention):

- Write each conclusion as a question in `conclusions` (at most 4): `{"key": "resumen",
  "question": "¿Qué línea empeoró y por qué?", "sources": [12], "max_sentences": 2}`, and put a
  slot in the HTML: `<p class="conclusion" data-conclusion="resumen">texto provisional</p>` (a
  `div`, `p` or `span`, plain text). The platform writes the answer in that slot from the numbers of
  its sources, and shows a mark when the text was not written for the numbers on screen.
- Never write the conclusion yourself, never send `locked` (a person fixes a text in the app), and
  never put numbers in the prose around it: the slot carries them.
- Each conclusion costs prepaid AI credits on every refresh (about 1.5–6 credits each).
  `precompute` on one choice parameter (at most 8 values) writes a conclusion per value on every
  refresh too: the cost grows with it, and the publish response gives the `estimate`.

## 4. Verify and refresh

- `bi_get_report` and check the numbers against the queries. Check the size of the HTML (Rule 7)
  and that every `kit.*` call in your script exists in the kit (Rule 8).
- Data refresh re-runs the saved queries and swaps the islands: `bi_refresh_report_data` (or the
  button in Reportes). The prose you write never updates, so keep fixed numbers out of it; the
  conclusion slots do update: a refresh rewrites them in the background with prepaid AI credits
  (locked ones excepted), and `estimate` says up to how many credits.
- Every refresh of a report with parameters or buttons stores an edition: its default parameters
  (the period resolved in the workspace's timezone), the numbers and the buttons' counts. Members pick
  past periods from the editions and filter by the parameters (a filtered view is never saved); the
  buttons move with each edition. A new report shows its buttons only after its first refresh.
- `bi_get_report_view(token, edition_id?, params?)` reads the numbers of an edition or a filter view,
  and its conclusions with their state. A filter view writes its own missing conclusions in the
  background, in credits: at most 30 a day per report (100 per workspace, 20 per person). Read
  them again a minute later.
- `bi_update_report_conclusion(token, action="lock", slot_key, text)` fixes a slot's text (no AI
  rewrites it, no credits); `unlock` lets the AI write it again. Approving a draft is done in the app.

## 5. Share

- **Members** of the workspace: `bi_share_report(token, emails)` mails them the report's link (a
  notification; the link already works for them). Any member may do it; an optional `note` goes to
  members only. `bi_list_report_shares` shows who has it, `bi_revoke_report_share` takes it back.
- **People outside the workspace** are shared with only from the Frepi app (the report's Compartir
  or the copilot's card), by a workspace administrator. Over MCP an outside address answers
  `report_share_outside_needs_app`: tell the operator to share it from the app. `invite_as_members`
  (default true) invites each outside address as a member of the workspace: the copilot's card says
  so before the operator confirms, they get one mail, create their account and land on the report as
  members. Pass `false` and they get the link only: they sign in, or ask for access from the report.
  MCP never invites. There is no guest or read-only outside mode, and no sharer text goes to an
  outside address.
- **Members see the whole workspace, conversations included.** Joining is a workspace grant
  (`ws_viewer` by default): the person sees every conversation their role allows, not only this
  report's. A request from the report («Solicitar acceso», or a public report's conversation
  button) is approved in Usuarios › Solicitudes. Only the app invites: the share dialog's toggle and the copilot's confirm card.
- Not on SSO organizations, not during a self-serve free trial, and at most 50 outside recipients
  per workspace per day.

## Rules that bite

- Links to conversations come from `list_tag_assignments(...).assignments[].links` (over MCP:
  `bi_list_tag_assignments`); never compose them by hand.
- Inline the real kit, never a custom `kit` object: the links and refresh depend on it.
- Keep the whole HTML under 300 KB (aim for 150 KB). Publish rejects HTML over 512 KB: that is
  the hard limit, not the target. Islands hold aggregates and short fields, never long text per
  row (full transcripts, message bodies). Link to the conversation instead, or split the report.
  A 2.8 MB report took tens of seconds to load.
- Call only the kit functions that exist (the closed list in Rule 8, `kit.table` and `kit.val`
  included). Publish rejects any other `kit.*` name. Render tables with
  `kit.table("id", rows, {columns: [...]})` and KPI text with `kit.val("id", value, format)`.
- Wrap the init script in `(function () { … })();` and never declare page-level globals named
  like window properties (`top`, `parent`, `name`, `self`, `status`, `length`, `origin`):
  `var top = …` silently keeps `window.top`.
- Brand: used by default (see step 1 and `references/marca.md`). Never put another company's colors,
  fonts or logo in a workspace's report.
