Integrating Jira with Lovable uses Edge Functions to proxy the Atlassian REST API v3 with API token authentication. Store your Atlassian email and API token in Cloud Secrets, create Edge Functions for issues, projects, sprints, and board data, and build custom dashboards in Lovable's React frontend. Jira also has an MCP personal connector for build-time AI context — letting the AI read your tickets while generating code, separate from the runtime Edge Function integration.
Why integrate Jira with Lovable?
Jira is used by millions of engineering and product teams to track issues, manage sprints, and plan roadmaps. While Jira has a powerful built-in interface, many teams need custom views — a client-facing status portal showing only selected tickets, a management dashboard combining Jira sprint data with other business metrics, an internal tool with a simplified interface for non-technical stakeholders, or an automated system that creates issues based on events in other parts of your app.
Lovable's relationship with Jira is actually two-layered. At runtime, your deployed app can communicate with Jira through Edge Functions — fetching ticket data, creating issues, updating statuses, and displaying sprint boards. Separately, Jira (via Atlassian) is also available as an MCP personal connector in Lovable, which is a build-time capability: when enabled in Settings → Connectors → Personal connectors → Atlassian, Lovable's AI can read your Jira tickets, epics, and roadmap documents to generate more accurate, context-aware code. These are independent and both valuable — enable the MCP connector while building, and use the Edge Function integration for your app's runtime Jira features.
Unlike Linear (which has a native Lovable runtime connector), Jira requires manual Edge Function setup. This is largely because Jira's enterprise feature set and deep customization options don't map cleanly to a one-size-fits-all connector — the Edge Function approach gives you full flexibility to work with custom fields, complex JQL queries, and Jira's extensive schema.
Integration method
Jira has no native Lovable runtime connector. All Jira API calls — fetching issues, projects, boards, and sprints — run through Supabase Edge Functions using Atlassian's API token authentication (base64-encoded email:token in the Authorization header). The Jira MCP personal connector is a separate, parallel capability that gives Lovable's AI access to your Jira tickets during the code generation process — it does not affect runtime behavior.
Prerequisites
- A Lovable project with Cloud enabled
- An Atlassian account with access to a Jira Cloud workspace
- An Atlassian API token — generate at id.atlassian.com/manage-profile/security/api-tokens
- Your Jira Cloud domain (e.g., your-company.atlassian.net)
- Your Jira project keys (e.g., 'PROJ', 'ENG') and optionally a board ID for Agile features
Step-by-step guide
Generate an Atlassian API token
Generate an Atlassian API token
Jira Cloud uses API tokens for programmatic authentication. Go to id.atlassian.com/manage-profile/security/api-tokens. Click 'Create API token'. Give it a descriptive label like 'Lovable App Integration'. Click 'Create' and immediately copy the token — it's displayed only once. The Jira REST API uses Basic authentication with a base64-encoded string of 'email:token'. Your Edge Function will handle this encoding using Deno's built-in btoa() function: btoa('your-email@company.com:your-api-token'). This base64 string goes in the Authorization header as 'Basic {encoded}'. Note the distinction between API tokens and OAuth 2.0. API tokens are simpler and work for personal integrations where your app accesses Jira as you. OAuth 2.0 (3-legged) is needed for apps where other users connect their own Jira accounts. For most custom dashboards and internal tools, API tokens are sufficient and much simpler to implement. Note your Jira Cloud domain (the part before .atlassian.net in your Jira URL, e.g., 'mycompany' if your Jira URL is mycompany.atlassian.net). You'll need this to construct API endpoint URLs in the format: https://mycompany.atlassian.net/rest/api/3/...
Pro tip: API tokens are scoped to your personal Atlassian account and have the same permissions as your user account in Jira. If you only want the integration to read certain projects, ensure your Jira user has appropriate project-level permissions rather than being a global admin.
Expected result: You have an Atlassian API token copied. You know your Atlassian account email and your Jira Cloud domain (e.g., mycompany.atlassian.net).
Store Jira credentials in Lovable Cloud Secrets
Store Jira credentials in Lovable Cloud Secrets
Open your Lovable project, click '+' in the top-right to access panels, then select 'Cloud'. Expand the Secrets section and add the following credentials. Add JIRA_EMAIL with your Atlassian account email address (the one you use to log into Jira). Add JIRA_API_TOKEN with the API token you just generated. Add JIRA_DOMAIN with your Jira Cloud domain, formatted as 'https://your-company.atlassian.net' (the full base URL without trailing slash). Your Edge Function will construct the Authorization header by base64-encoding 'JIRA_EMAIL:JIRA_API_TOKEN' using Deno's btoa() function. The Jira REST API v3 base path is /rest/api/3/ for core Jira operations, and /rest/agile/1.0/ for Agile (sprint/board) features. The Jira API does not require CORS workarounds for well-structured Edge Functions since the function makes server-to-server requests. However, your frontend's calls to the Edge Function still need CORS headers on the Edge Function response — ensure your function returns Access-Control-Allow-Origin: * headers. Lovable's security infrastructure blocks approximately 1,200 hardcoded API keys daily. API tokens are high-value credentials — they grant full access to your Jira workspace as your user account. Always use Cloud Secrets and never paste them in Lovable chat.
Pro tip: If your Jira instance requires IP allowlisting (common in Enterprise plans), add Supabase's Edge Function IP ranges to the allowlist. Check your Supabase project settings under 'Project Settings → Database' for the relevant IP ranges, or upgrade to a Supabase plan that provides static IP addresses.
Expected result: JIRA_EMAIL, JIRA_API_TOKEN, and JIRA_DOMAIN are set in Cloud Secrets. No credentials appear in any source code file.
Create the Jira API proxy Edge Function
Create the Jira API proxy Edge Function
Build the Edge Function that handles Jira API calls. This function constructs the Basic auth header from your credentials, and proxies different Jira operations based on the action parameter. The Jira REST API v3 has rich filtering through JQL (Jira Query Language). JQL is a powerful SQL-like language for querying issues: 'project = PROJ AND status = "In Progress" AND sprint in openSprints() ORDER BY priority DESC'. Your Edge Function should accept a JQL string as a parameter, URL-encode it, and pass it to the /rest/api/3/search endpoint. This gives you complete flexibility to filter issues any way Jira supports. For board and sprint data, use the Agile API at /rest/agile/1.0/. The key endpoints are /board (list boards), /board/{boardId}/sprint (list sprints), and /board/{boardId}/sprint/{sprintId}/issue (issues in a specific sprint). Sprint boards require knowing the board ID — you can fetch it once and store it in Cloud Secrets as JIRA_BOARD_ID. For issue creation, the POST /rest/api/3/issue endpoint accepts a fields object with project key, issuetype, summary, description, priority, assignee, and custom field values. Custom fields use keys like 'customfield_10016' for story points (the exact key varies by Jira configuration — fetch it from /rest/api/3/field).
Create a Supabase Edge Function at supabase/functions/jira/index.ts. It should read JIRA_EMAIL, JIRA_API_TOKEN, and JIRA_DOMAIN from Deno.env and construct a Basic auth header using btoa(email + ':' + token). Support these actions via POST body: action='search-issues' takes a jql string (e.g. 'project = PROJ AND status != Done') and maxResults (default 50), calls GET /rest/api/3/search with the JQL query, returns the issues array with id, key, summary, status.name, priority.name, assignee.displayName, updated fields; action='get-boards' fetches GET /rest/agile/1.0/board and returns board list; action='get-sprint-issues' takes boardId and sprintId, fetches GET /rest/agile/1.0/board/{boardId}/sprint/{sprintId}/issue; action='create-issue' takes projectKey, summary, issueType, description and creates via POST /rest/api/3/issue. Add CORS headers.
Paste this in Lovable chat
1// supabase/functions/jira/index.ts2import { serve } from "https://deno.land/std@0.168.0/http/server.ts";34const JIRA_DOMAIN = Deno.env.get("JIRA_DOMAIN") ?? "";5const AUTH = btoa(`${Deno.env.get("JIRA_EMAIL")}:${Deno.env.get("JIRA_API_TOKEN")}`);6const corsHeaders = { "Access-Control-Allow-Origin": "*", "Access-Control-Allow-Methods": "POST, OPTIONS", "Access-Control-Allow-Headers": "Content-Type, Authorization" };78const jiraHeaders = {9 Authorization: `Basic ${AUTH}`,10 Accept: "application/json",11 "Content-Type": "application/json",12};1314serve(async (req) => {15 if (req.method === "OPTIONS") return new Response(null, { headers: corsHeaders });16 try {17 const { action, jql, maxResults, boardId, sprintId, issue } = await req.json();1819 if (action === "search-issues") {20 const encodedJql = encodeURIComponent(jql ?? "ORDER BY updated DESC");21 const res = await fetch(`${JIRA_DOMAIN}/rest/api/3/search?jql=${encodedJql}&maxResults=${maxResults ?? 50}&fields=summary,status,priority,assignee,updated,issuetype,customfield_10016`, { headers: jiraHeaders });22 const data = await res.json();23 return new Response(JSON.stringify({ issues: data.issues ?? [], total: data.total }), { headers: { ...corsHeaders, "Content-Type": "application/json" } });24 }2526 if (action === "get-boards") {27 const res = await fetch(`${JIRA_DOMAIN}/rest/agile/1.0/board`, { headers: jiraHeaders });28 const data = await res.json();29 return new Response(JSON.stringify({ boards: data.values ?? [] }), { headers: { ...corsHeaders, "Content-Type": "application/json" } });30 }3132 if (action === "get-sprint-issues") {33 const res = await fetch(`${JIRA_DOMAIN}/rest/agile/1.0/board/${boardId}/sprint/${sprintId}/issue?fields=summary,status,priority,assignee,customfield_10016`, { headers: jiraHeaders });34 const data = await res.json();35 return new Response(JSON.stringify({ issues: data.issues ?? [] }), { headers: { ...corsHeaders, "Content-Type": "application/json" } });36 }3738 if (action === "create-issue" && issue) {39 const res = await fetch(`${JIRA_DOMAIN}/rest/api/3/issue`, {40 method: "POST",41 headers: jiraHeaders,42 body: JSON.stringify({43 fields: {44 project: { key: issue.projectKey },45 summary: issue.summary,46 issuetype: { name: issue.issueType ?? "Task" },47 description: issue.description ? {48 type: "doc", version: 1,49 content: [{ type: "paragraph", content: [{ type: "text", text: issue.description }] }]50 } : undefined,51 priority: issue.priority ? { name: issue.priority } : undefined,52 }53 }),54 });55 const data = await res.json();56 return new Response(JSON.stringify({ key: data.key, id: data.id }), { headers: { ...corsHeaders, "Content-Type": "application/json" } });57 }5859 return new Response(JSON.stringify({ error: "Unknown action" }), { status: 400, headers: corsHeaders });60 } catch (err) {61 return new Response(JSON.stringify({ error: err.message }), { status: 500, headers: { ...corsHeaders, "Content-Type": "application/json" } });62 }63});Pro tip: Jira's REST API v3 description field uses Atlassian Document Format (ADF) — a JSON structure rather than plain text or Markdown. The code above wraps a plain text description in the minimal ADF structure needed. For rich text with formatting, code blocks, or bullet lists, you'll need to construct the full ADF document. Atlassian provides an ADF Builder library for this.
Expected result: The jira Edge Function is deployed. Calling it with action='search-issues' and jql='project = PROJ ORDER BY updated DESC' returns a list of Jira issues with key, summary, status, and assignee.
Enable the Jira MCP personal connector for AI-assisted building
Enable the Jira MCP personal connector for AI-assisted building
In addition to the runtime Edge Function integration, Lovable offers a Jira MCP personal connector through the Atlassian integration. This is a build-time feature, not a runtime feature — it gives Lovable's AI context from your Jira tickets while you're building, helping it generate code that matches your actual requirements. To enable it: in Lovable, click 'Settings' (gear icon) in the top navigation, then 'Connectors', then 'Personal connectors'. Click 'Atlassian (Jira + Confluence)'. You'll go through an OAuth2 flow to connect your Atlassian account. Once connected, Lovable's AI can read your Jira tickets, epics, and linked Confluence pages when generating code. With the MCP connector active, you can reference Jira tickets directly in chat by typing @jira or including ticket numbers in your prompts. For example: 'Implement the feature described in PROJ-245' or 'Create a form that handles the edge cases listed in ticket BUGS-89'. The AI reads the ticket description, acceptance criteria, and linked documents to generate more accurate implementations. The MCP connector reads your Jira data at build time and does not affect your deployed app's behavior. The Edge Function integration (Steps 1-3) is what powers your app at runtime for actual users. Both can be active simultaneously — MCP helps you build faster, Edge Functions power the running app.
Pro tip: After enabling the Atlassian MCP connector, try prompting Lovable with a specific Jira ticket number (e.g., 'Build a UI for the requirements in PROJ-123'). The AI will read the ticket and generate code aligned with your actual requirements rather than a generic interpretation.
Expected result: The Atlassian personal connector appears as 'Connected' in Settings → Connectors → Personal connectors. In the Lovable chat, you can reference Jira ticket numbers and the AI incorporates ticket context into code generation.
Build a custom Jira dashboard in Lovable
Build a custom Jira dashboard in Lovable
With the Edge Function deployed, build a custom project management view. Ask Lovable to create a dashboard that displays your most important Jira data in a layout tailored to how your team actually works — not Jira's default interface. For a sprint-focused team, the most valuable dashboard elements are: current sprint progress (story points completed vs total), issues grouped by assignee with their status, a list of blockers (issues with status 'Blocked' or with blocker links), and recently updated issues. For a customer-support team, the most valuable elements might be open bugs sorted by priority, a count by severity, and mean time to resolution from your issue creation and resolution dates. Jira's status field names ('To Do', 'In Progress', 'Done', 'In Review', 'Blocked') vary by project configuration — don't hardcode status names. Instead fetch unique status values from your issues and let users configure which statuses map to 'todo', 'in-progress', and 'done' categories in your dashboard. For issue creation from your Lovable app, build a quick-add form that creates Jira issues without leaving your interface. This is useful for customer feedback collection, bug report submission, and feature request tracking — users submit in your app, Jira tickets appear automatically for your engineering team.
Build a Jira project dashboard at /projects. On load, call the jira Edge Function with action='search-issues' and jql='project = PROJ AND sprint in openSprints() ORDER BY priority ASC'. Group issues by status in a Kanban-style board with drag-to-update status (call jira with action='update-status' on drop). Show each issue card with: key, summary, assignee avatar, priority indicator, and story points. Add a quick-create form at the top: summary, issue type, and priority fields that call action='create-issue' and add the new issue to the board instantly.
Paste this in Lovable chat
Pro tip: Jira issue keys (like PROJ-123) are clickable links to the issue in Jira. Append the issue key to your Jira domain to construct the link: https://your-company.atlassian.net/browse/PROJ-123. This lets users click through from your Lovable dashboard to the full Jira issue when they need more detail.
Expected result: The dashboard displays current sprint issues grouped by status. Issue cards show key, summary, assignee, and priority. The quick-create form successfully creates new Jira issues visible in both Lovable and Jira.
Common use cases
Client-facing project status portal
A development agency wants to give clients a branded portal showing the status of their project's Jira tickets without giving them access to the full Jira workspace. The Edge Function fetches issues from a specific Jira project filtered by a client-specific label, and the Lovable frontend shows a clean timeline view of what's in progress, completed, and upcoming.
Build a client project status portal at /status. The Edge Function should fetch all Jira issues from project KEY='CLIENTABC' using JQL: 'project = CLIENTABC ORDER BY status ASC, updated DESC'. Return summary, status, priority, assignee name, and last updated date for each issue. Display them in three columns: To Do, In Progress, Done. Add a filter dropdown to show only specific issue types (Bug, Story, Task).
Copy this prompt to try it in Lovable
Engineering sprint dashboard with velocity metrics
An engineering manager wants a custom sprint dashboard showing the current sprint's progress, team velocity over the last 5 sprints, and a burndown view — data that exists in Jira but requires navigating multiple screens to assemble. The Edge Function queries the Jira Agile API for sprint data and the frontend renders it in a single comprehensive view.
Create an engineering dashboard at /sprints showing the active sprint for board ID 42. Fetch: 1) the active sprint details (name, start/end date, goal), 2) all issues in the sprint with their status, story points, and assignee, 3) sprint velocity by fetching the last 5 completed sprints and their story point totals. Display a progress bar showing completed vs total story points, a per-assignee breakdown, and a 5-sprint velocity chart.
Copy this prompt to try it in Lovable
Automated bug report creation from user feedback
A SaaS app wants users to be able to submit bug reports through an in-app form that automatically creates Jira issues. The form captures the bug description, steps to reproduce, browser/OS info, and a screenshot URL. The Edge Function creates a properly formatted Jira issue with the correct project, issue type, priority, and labels based on the feedback content.
Add a 'Report a Bug' button to my app that opens a feedback form. The form should have fields: title, description (with steps to reproduce), severity (Low/Medium/High/Critical). On submit, call an Edge Function that creates a Jira issue in project 'BUGS' with issuetype='Bug', priority mapped from severity, a label 'user-reported', and description formatted with the user's email, browser, and submitted details. Show the user the created issue key (like BUGS-123) as a reference number.
Copy this prompt to try it in Lovable
Troubleshooting
Jira API returns 401 Unauthorized even with correct credentials
Cause: The Basic auth header is being constructed incorrectly — the email and API token must be concatenated with a colon separator before base64 encoding. Using just the token without the email, or encoding them separately, produces an invalid credential.
Solution: Verify the auth construction in your Edge Function: btoa(email + ':' + apiToken) where email is the full email address and apiToken is the full token string. Test by constructing the auth string in a browser console and checking the decoded result. Also verify JIRA_DOMAIN does not have a trailing slash.
1// Correct Basic auth construction2const email = Deno.env.get("JIRA_EMAIL") ?? "";3const token = Deno.env.get("JIRA_API_TOKEN") ?? "";4const auth = btoa(`${email}:${token}`);5const authHeader = `Basic ${auth}`;JQL search returns 400 Bad Request with 'The value X does not exist for the field Y'
Cause: The JQL query references a project key, status name, issue type, or custom field value that doesn't exist in your Jira instance. JQL field values are case-sensitive and must exactly match your Jira configuration.
Solution: Fetch the valid values for your Jira instance: GET /rest/api/3/project for project keys, GET /rest/api/3/issuetype for issue types, GET /rest/api/3/status for status names, and GET /rest/api/3/field for custom fields. Use these exact values in your JQL queries. Wrap string values with spaces in double quotes: status = "In Progress" not status = In Progress.
Agile board and sprint endpoints return 404 Not Found
Cause: Your Jira account may not have the Software license (Jira Software) enabled — the Agile API (/rest/agile/1.0/) is only available in Jira Software, not Jira Work Management. Also, the board ID may be incorrect or the user doesn't have access to that board.
Solution: Confirm your Jira workspace has Jira Software (look for 'Boards' in the sidebar). Fetch available boards with GET /rest/agile/1.0/board to find valid board IDs. If the endpoint returns 404 entirely, check if your Jira workspace type includes the Software plan — Work Management teams use a different project structure without sprints.
Issue creation fails with 'Field description is not on the appropriate screen'
Cause: Jira's issue creation screen may not have all fields configured. Jira project administrators control which fields appear on the create issue screen, and sending fields not on that screen returns this error.
Solution: Start with only the required fields (project, summary, issuetype) and add optional fields one at a time to identify which one is blocked. Ask your Jira project administrator to add the desired field to the Create Issue screen, or use the project settings in Jira to check screen configurations. Alternatively, create the issue without the problematic field and update it separately via PUT /rest/api/3/issue/{issueId}.
Best practices
- Store only the Atlassian API token and email in Cloud Secrets — never hardcode them in Edge Function code or Lovable chat messages. API tokens provide full Jira access as your user account.
- Use JQL (Jira Query Language) for flexible issue filtering rather than fetching all issues and filtering in JavaScript — JQL runs server-side in Jira and returns only the relevant subset, reducing data transfer.
- Cache Jira issue data in Supabase for non-real-time dashboards. Jira API has rate limits and fetching the same data on every user page load is inefficient. A 5-minute cache is suitable for most reporting use cases.
- Include specific fields in your API requests using the fields parameter (?fields=summary,status,assignee,priority) rather than receiving all fields. Jira issues can have 50+ fields — fetching only what you display reduces response size significantly.
- Use the Jira MCP personal connector in Lovable settings to give the AI context from your actual Jira tickets while building. This produces code that matches your real requirements better than generic prompts.
- Handle pagination for large issue sets — the Jira search API returns at most 100 issues per request (or your configured maxResults). Use the startAt parameter to paginate through all results when building reports.
- When creating issues programmatically, use issue links (POST /rest/api/3/issueLink) to connect related issues — for example, linking a user-reported bug to an existing feature request keeps your Jira data organized.
Alternatives
Linear is a native Lovable shared connector with gateway architecture and automatic token management, making it significantly faster to integrate than Jira's manual Edge Function approach.
Monday.com uses a board/column paradigm with a GraphQL API — it's better for non-engineering teams who prefer visual boards over Jira's issue-centric model.
ClickUp has a deep task hierarchy and REST API similar to Jira but with a more flexible structure suitable for teams that need both project management and docs in one tool.
Frequently asked questions
What's the difference between the Jira MCP connector and the Edge Function integration?
The Jira MCP personal connector (available in Settings → Connectors → Personal connectors → Atlassian) gives Lovable's AI read access to your Jira tickets during the building process — it helps the AI generate better code by understanding your actual requirements. It does not affect your deployed app at runtime. The Edge Function integration is what your deployed app uses to fetch and display Jira data at runtime for actual users. You need both for the full experience: MCP for smarter code generation, Edge Functions for runtime functionality.
Does Jira's API work for both Jira Cloud and Jira Server/Data Center?
This tutorial covers Jira Cloud (hosted at atlassian.net). Jira Server and Data Center use a different API path (/rest/api/latest/ instead of /rest/api/3/) and different authentication methods (session-based or Personal Access Tokens). The REST API v3 endpoints described here are exclusive to Jira Cloud. If your team uses Jira Data Center, the authentication and some endpoint structures differ.
Can I update issue status through the API?
Yes, but status transitions in Jira use a transition ID, not the status name directly. First fetch the available transitions for an issue with GET /rest/api/3/issue/{issueKey}/transitions, then execute a transition with POST /rest/api/3/issue/{issueKey}/transitions and the transition ID. Transition IDs are specific to each Jira workflow and project — you need to fetch them at runtime rather than hardcoding them.
How do I handle custom fields in Jira API responses?
Jira custom fields appear as customfield_XXXXX keys in the API response (e.g., customfield_10016 for Story Points). The exact key varies per Jira instance. Fetch the field list from GET /rest/api/3/field to map field names to their keys for your instance. Once you have the key, include it in the fields parameter of your search requests and access it from the response object.
Is the Jira API rate limited?
Yes. Jira Cloud enforces rate limits based on your plan — Cloud Standard and Premium plans have higher limits than Free. The general limit is around 40,000 API calls per hour for most plans. For burst usage from multiple simultaneous users, add caching in Supabase to reduce direct Jira API calls. Jira returns a Retry-After header when rate limits are hit — implement exponential backoff in your Edge Function for robust production behavior.
Talk to an Expert
Our team has built 600+ apps. Get personalized help with your project.
Book a free consultation