EC · ENRIQUE CHYRNIA / FIL-GRAMA.PRT ← Home
FIL-GRAMA.PRTSHEET 01/10 · THE PROBLEMBLUEBAR · 132 COL

01Why I built Fil-Grama

An agency with five or six clients needs to compare accounts and see how each one moved over the months. The networks don't help there: every API gives a limited window and past that the data is simply not available. Without a capture process of your own, that history never exists.

Fil-Grama saves it day by day — reach, followers, interactions, engagement, per account, in its own tables. That's what the dashboards, the account comparison and the reports read from.

Its own captureA daily snapshot per account, beyond the window each network gives.
Several clients, three networksEvery client of the agency with their accounts in one panel.
Reports and comparisonDashboards, comparison between accounts, PDF per client and date range.

What the API returns, and what Fil-Grama kept

Same account, same metric. The difference is whether somebody was writing it down.

ohmybunny · instagram · followers at month end
1.180
1.226
1.290
1.341
1.402
1.488
1.553
1.607
1.690
1.772
1.861
1.934
aug sep oct nov dec jan feb mar apr may jun jul
— no record · outside the API window —

The eleven months on the left are there because the job saved them. If I started the project today, that stretch would stay blank and there's no way to fill it in later.

FIL-GRAMA.PRTSHEET 02/10 · THE DAILY JOBBLUEBAR · 132 COL

02The morning round

One pass per account, once a day, six in the morning Asunción time. Every result gets written down, the failures too: which account, which error, at what time. An account that fails doesn't get retried in a loop; it's recorded in sync_account_results and the run keeps going.

Fil-Grama's blue quail
This is the product's real loading indicator
sync_runs · run #412
06:00:00daily job · start · 3 accounts queued
06:00:11ohmybunny · instagram → snapshot saved (reach 3.412 · foll. 1.934)
06:00:19ohmybunny · facebook → snapshot saved (reach 861 · foll. 402)
06:00:28ohmybunny · tiktok → snapshot saved (views 12.907 · foll. 318)
06:00:34meta token refreshed · expires in 58 days
06:00:41done · 3 accounts · 0 errors · written to sync_runs
FIL-GRAMA.PRTSHEET 03/10 · ARCHITECTUREBLUEBAR · 132 COL

03Modular backend, contract as the source of truth

Backend in Java with Spring Boot, split by domain: auth, clients, accounts, OAuth, Meta integration, metrics, sync, reports, media, storage. Postgres with versioned migrations. The React frontend doesn't hand-write its calls to the API — they're generated from the contract with Orval, so a change in the backend shows up as a type error while I'm working and not as a bug in production.

Social networks TikTok API Meta Graph API Daily job metrics sync and OAuth Backend Auth · Accounts Metrics · Sync Reports · Media domain modules · Spring Boot Data & files PostgreSQLmetrics · clients Storage S3R2 prod · MinIO dev Client Frontend Reactdashboards · reports JWT auth (access + rotated refresh) · embedded MCP server with read-only tools over reports Conceptual, simplified diagram. It follows the backend's real modules, without infrastructure details.

Refresh token rotation

If a stolen refresh token gets reused, the whole family goes down, not just that token.

Login Access (15 min)+ Refresh (family A) Uses refresh →rotates: new access+refresh old refresh reused Reuse detectedthat token had already rotated Family A revokedevery session in the chain 401 A refresh token that already rotated shouldn't come back. If it does, I treat it as stolen.

Data model — core

There are 14 tables in the schema. This is the part the dashboard and the daily job depend on.

clientstimezone · archived usersadmin/employee role employee_client_priority social_accountsIG · FB · TikTok postsper account account_metric_snapshotsappend-only, daily post_metric_snapshotsappend-only sync_runsone daily job sync_account_resultsper account, per run Every table carries client_id. That's what lets it turn multi-tenant later without redoing the model.
FIL-GRAMA.PRTSHEET 04/10 · ENGINEERINGBLUEBAR · 132 COL

04Backend details

  • 001
    167+ testsIncludes e2e against a real Postgres with Testcontainers, not only mocks. They take longer to run and they caught things mocks never would have.
  • 002
    Refresh tokens with rotationIf a stolen token gets reused, I detect it and revoke the whole family, not just that token.
  • 003
    OAuth tokens encrypted at restAES-GCM. They never sit in the database in plain text and they never come back out through the API.
  • 004
    Errors in RFC 7807One exception handler, the same problem+json shape everywhere. The frontend reads one format and nothing else.
  • 005
    Multi-tenant from the model14 tables with client_id. It took me a few passes to get the model right, and it's the reason going SaaS doesn't mean rewriting it.
FIL-GRAMA.PRTSHEET 05/10 · INTEGRATIONSBLUEBAR · 132 COL

05TikTok and Meta, real status

Both integrations are written. Neither one is running against real client accounts yet, and the reason is different in each case.

TikTok — OAuth + metrics[ OK ] full sandbox

OAuth end to end with PKCE, token refresh with rotation, and a QR flow for connecting an account. It's the one I tested from top to bottom, from authorizing to reading metrics. Publishing needs a separate audit from TikTok that I haven't asked for yet.

Meta — Instagram / Facebook[PEND] App Review

The code is there, OAuth and metrics reading, but Meta asks for App Review with Advanced Access plus business verification before you can touch real accounts. Today it runs in Development mode with my own test accounts. What's missing is the paperwork, not the code.

FIL-GRAMA.PRTSHEET 06/10 · STATUSBLUEBAR · 132 COL

06Where it stands

There are no clients in production yet. I'd rather have the base settled before someone else's data lives in there: integrations enabled, a boring deploy, backups I actually restored. Next one up is Meta approved.

Declared status · jul 2026
[x]Backend and frontend deployed — VPS on Vultr and Cloudflare Pages, running.
[x]TikTok integrated — OAuth with PKCE and metrics reading in sandbox.
[~]Meta in App Review — business verification in progress.
[~]Automated reports — PDF per client and date range.
[ ]Content publishing — waiting on TikTok's audit.
[ ]Multi-tenant SaaS — from a tool of mine to a product.
FIL-GRAMA.PRTSHEET 07/10 · INFRASTRUCTUREBLUEBAR · 132 COL

07How it's deployed

I rented the server, wired the connections and deployed both halves myself: backend on a Vultr VPS, Docker Compose behind Caddy with Postgres, and frontend on Cloudflare Pages, which redeploys on every push to main.

For development I open a Cloudflare tunnel to the local backend, so I test against a real domain without exposing my machine. Backups run and every so often I restore one: a backup you never tested isn't a backup. This page has no IPs, credentials, tokens or internal URLs.

BackendVPS Vultr · Docker Compose · Caddy · PostgreSQL
FrontendCloudflare Pages · redeploy on git push
StorageCloudflare R2 (S3-compatible) in prod · MinIO in dev
FIL-GRAMA.PRTSHEET 08/10 · TOURBLUEBAR · 132 COL

08The screens, browsable

A selection out of the 50 screens I designed: analytics, comparison between accounts, posts, reports, platform. Each card opens the real screen, desktop and mobile.

From wireframe to high fidelity

The same screen, Audience, before and after the visual pass.

Low-fidelity wireframe of the Audience screen
Phase 1 · Wireframe — structure and hierarchy, no color.
High-fidelity version of the Audience screen
Phase 2 · High fidelity — palette, type and components applied.
FIL-GRAMA.PRTSHEET 09/10 · PRODUCTBLUEBAR · 132 COL

09Screenshots of it running

The app in production, not mockups: the client dashboard, a post in detail, the comparison between accounts, and the report generator that exports to PDF.

Client dashboard in Fil-Grama with reach, interactions, engagement and follower metrics
Fig. 01 — Client dashboard: reach, interactions, engagement and followers, over the history the daily job built.
Detail view of an Instagram post with its comment metric over time
Fig. 02 — Post detail: real content from the Instagram API, with one metric's evolution over time.
Reach and follower comparison between a client's connected accounts, in a bar chart
Fig. 03 — Compare accounts: reach, followers, interactions and engagement side by side, as a table or bars.
Automated per-client report generator with KPIs by social network, exportable to PDF or Markdown
Fig. 04 — Automated report: KPIs by network with the change against the previous period, exported to PDF or Markdown.

The screenshots come from Oh My Bunny, my own venture. I use it as a real test account while Meta's approval moves along. The numbers are small because the account is small and was connected recently.

FIL-GRAMA.PRTSHEET 10/10 · STACKBLUEBAR · 132 COL

10Tech inventory

Backend

  • Java · Spring Boot
  • REST APIs · JWT

Data

  • PostgreSQL · Flyway
  • Relational modeling

Frontend

  • React 19 · TypeScript
  • Vite · TanStack Query

Integrations

  • OAuth 2.0 · PKCE
  • TikTok API · Meta Graph API

Infrastructure

  • Vultr VPS · Docker Compose
  • Caddy · Cloudflare Pages · R2

AI

  • Embedded MCP server
  • Read-only tools over reports