From source to Instagram feed in minutes β AI writes the copy, a human always approves.
An open-source web tool that lets a newsroom publish breaking-news posts to Instagram with almost no friction: paste a police report, a press release or a link, and get a ready-to-review post with a title, subtitle and caption β built on a pipeline designed not to invent facts.
Every story in one feed: paste a source β AI drafts the post β frame the photo β a human approves and publishes.
TL;DR: paste a text, an article link or a PDF, optionally credit someone (πΈ
@photographer), and hit Generate. The AI writes the title, subtitle and caption β and never touches the photo.
TL;DR: drag and zoom the photo, drag the title or subtitle to reposition them, edit the words (69 / 149 character limits). The brand template stays intact; only this post changes. What you see is exactly what the server renders.
Video (Reels) works too. Upload a clip instead of a photo and JornAI handles the boring parts:
- Normalizes any video to 9:16 β already-vertical clips are cropped top and bottom; landscape clips are kept whole over a blurred, mirrored backdrop instead of ugly black bars.
- Keeps the title card and logo inside Instagram's safe zones, so the profile, caption and buttons never cover them.
- Burns in an animated title card (slide + fade) in one of three styles, using your brand colors.
- Fixes phone-recorded videos that arrive rotated.
TL;DR: add up to 10 photos and the post becomes an Instagram carousel. The 1st photo is the cover, with the template, title and subtitle; the others go out as the bare photo, cropped to the same proportion (Instagram requires it). Reorder, swap the cover and frame each slide.
TL;DR: every photo β cover or slide β has an Edit image popup, framed at the real proportion of the art:
- Blur image β tap or drag over the photo to hide faces, plates or anything else (with undo). Handy for accident and police photos.
- Adjust β zoom, straighten, rotate, mirror, and blurred borders: show the whole photo and fill the rest of the frame with a mirrored, blurred copy, the same look as the videos.
- Color β brightness, contrast and saturation.
TL;DR: see the post exactly as it will look on Instagram (carousels can be swiped), then Approve and publish, Request rewrite (the AI writes a new version) or Reject with a reason. Every change is a new version, so the full history is kept.
- Add to story too β a checkbox under the approve button, always unticked by default. When ticked, once the feed post is out the art (carousel: cover only) or the rendered video is also published as an Instagram story.
- If the story fails, the feed post stays up β the failure is recorded and the review screen says why.
TL;DR: the admin area is where a newsroom makes JornAI sound and look like itself.
- Newsroom style β register real posts from your newsroom; the AI learns the tone and format (it never copies the content).
- Templates β build photo templates visually (see below).
- Users & roles β admin, manager and reporter, each company isolated from the others.
- API keys, Company, Settings, AI balance β connect providers, set your logo and brand colors, toggle mandatory review, watch your AI spend.
TL;DR: the photo template is the heart of JornAI, and it is fully customizable β upload your brand frame (a transparent PNG made in Canva or Photoshop), drag the title and subtitle boxes where you want them, and pick the format (4:5 or 1:1), font size, color, weight, alignment and case. Video is simpler on purpose: it ships with 3 ready-made title-card styles (classic, light, bold) that pick up your brand colors.
Newsrooms often need to get a short, urgent post out β an accident, a weather alert, a local event β with almost no friction, but still with editorial review and consistent branding. JornAI is not a generic social-media scheduler and not a raw AI text generator with no guardrails:
- π§Ύ Fact-first. Sources are cleaned, redacted and checked before and after the model, so a confident-sounding article doesn't come with invented details.
- π Always human-approved. Nothing reaches Instagram without a person approving it.
- π¨ Consistent branding. Photo + text are composed on a fixed template, in the browser and on the server, with identical results.
- π Works in your language. One environment variable picks the language of the sources and the posts (
enandptincluded, more welcome). - π Runs with no keys.
AI_PROVIDER="mock"+ local storage lets you try the whole flow without any external service.
Source (photo, text, link, or PDF)
β
βΌ
AI writes the copy (title, subtitle, caption)
β
βΌ
Reporter frames the photo(s) or video in the brand template
(single photo, carousel of up to 10, or a 9:16 Reels video)
β
βΌ
Editor reviews β approve / reject / regenerate / edit
(optional: "Add to story too")
β
βΌ
Publish to Instagram (feed / carousel / Reels, + story if ticked)
- AI only writes text β it never generates or picks images. Photos are always real and chosen by a person.
- Every AI generation (or manual edit) creates a new version, so nothing is overwritten and there is a full history.
- Photo, carousel (up to 10 photos) and video (Reels) posts, with an animated title card for video.
- Built-in image editor: blur faces, blurred borders, rotate/straighten, color.
- Optional story share on approval β always an explicit, per-post choice.
- Peer review: a reporter's post can be approved by a manager/admin, or by two colleagues.
- Multi-tenant: each company has its own feed, templates, style examples and settings.
Feeding a police report or an official PDF straight into an LLM is a good way to get a confident-sounding article with wrong facts. JornAI's pipeline is built around not letting that happen:
- Source text is cleaned and compacted deterministically (no AI) before it ever reaches a model β official documents/forms get their real fields extracted (who, where, cause, injuries) instead of being blindly truncated.
- Personal data (names, national IDs, phone numbers, plates) is redacted before the AI ever sees it.
- The system prompt has explicit, tested rules against inventing details, generic filler, or unsupported claims.
- Generated content is checked against the source by a deterministic validator after generation (e.g. the weekday must match the source date, no unsupported claims about investigations, road closures or deaths); a violation triggers one corrective regeneration before failing loudly.
- The weekday is computed by code, never by the model.
JornAI works in any language β one per deployment: the language of the sources and of the generated posts, chosen with the APP_LANGUAGE environment variable. Each language is a small pack with its own rules for source cleanup, personal-data redaction and the fact validator. Two ship today, as examples:
| Value | Example of what the pack covers |
|---|---|
en (default) |
Posts in English; English rules (SSN, US phone numbers, mm/dd/yyyy dates). |
pt |
Posts in Brazilian Portuguese; Brazilian rules (CPF, plates, police-report forms, dd/mm/yyyy dates). |
The prompts themselves are written in English; only the examples and rules the model must reproduce live in the language pack (src/lib/language/<lang>/). To add your language, add a pack there and register it in src/lib/language/index.ts β see CONTRIBUTING.md. APP_LANGUAGE also sets the default interface language (each person can still switch it in the UI); the code, comments and logs are always English.
- Next.js (App Router) + TypeScript, PostgreSQL via Prisma
- Fabric.js for the in-browser art editor, Sharp for the server-side final render, ffmpeg for video
- AI: any OpenAI-compatible or Anthropic text provider (OpenRouter, Groq, Gemini, OpenAI, Claude), with automatic fallback across providers
- Instagram Graph API for publishing (single image, carousel, Reels and stories); S3/R2-compatible storage (or local disk in dev)
| Role | Can do |
|---|---|
| admin | Everything β users, API keys, templates, style examples, approve/publish |
| manager | Approve/reject/regenerate/edit/publish; manage templates and style examples |
| staff (reporter) | Submit sources, edit own drafts before approval; approve a colleague's post (two approvals publish it) |
git clone https://github.com/MatheusBomtempo/JornAI.git
cd JornAI
npm install
docker compose up -d # PostgreSQL that matches .env.example (or bring your own)
cp .env.example .env # fill in DATABASE_URL and, optionally, an AI provider key
npm run db:migrate
npm run db:seed # sample company + accounts, dev only
npm run devOpen http://localhost:3000 and sign in with admin@jornai.local / admin12345 (created by the seed, development only).
No AI key yet? Keep AI_PROVIDER="mock" in .env to run the whole flow with a stub AI response β useful for trying the app without any external service.
Two kinds of configuration, done once, in this order:
Everything technical is configured through environment variables β see .env.example, which documents each one. The important ones:
| Variable | What it does |
|---|---|
APP_LANGUAGE |
Working language (en and pt ship as examples; add your own pack) |
DATABASE_URL / DIRECT_URL |
PostgreSQL connection |
AUTH_SECRET |
Signs the session JWTs |
AI_PROVIDER |
mock, chain (fallback across providers), openrouter, groq, gemini, openai, anthropic |
STORAGE_PROVIDER |
local (dev) or s3 (S3 / Cloudflare R2 / Supabase) |
IG_USER_ID / IG_ACCESS_TOKEN |
Instagram Graph API publishing |
TL;DR: before the first post, open Admin and make JornAI look and sound like your newsroom. It takes a few minutes and you only do it once.
- Company β your name, logo, Instagram
@and brand colors (the logo and colors show up on the art and on the video title cards). On the very first login the admin is guided through this. - Templates β build your photo frame with the template builder: 4:5 or 1:1, your overlay PNG, title and subtitle positions and text style.
- Newsroom style β paste a few real posts so the AI learns your tone and format.
You can come back and change any of these later.
The project deploys as a regular Next.js app (it is developed against Vercel + Neon + Cloudflare R2). npm run build applies the Prisma migrations, so the build needs the database configured.
SPEC.mdβ full technical spec: database schema, API endpoints, state machine, Instagram publishing.CONTRIBUTING.mdβ how to contribute and how to add a language.SECURITY.mdβ how to report a vulnerability.
- More language packs (Spanish, French, β¦)
- More official-document profiles (the way police-report forms are cleaned today)
- Other social networks (deliberately out of scope for now)
Not planned, on purpose: scheduling, AI image generation, and publishing without human approval.
Issues and pull requests are welcome β read CONTRIBUTING.md first. If JornAI is useful to you, a β helps other newsrooms find it.
MIT β see LICENSE.






