Connect Bubble to Jira Cloud using the API Connector with HTTP Basic Auth — a Base64-encoded string of your account email and API token in the Authorization header, marked Private. The #1 trap: Jira Cloud v3 requires Atlassian Document Format (ADF) for the description field, not plain text. Sending a plain string returns HTTP 400 on every issue creation attempt.
| Fact | Value |
|---|---|
| Tool | JIRA |
| Category | Productivity |
| Method | Bubble API Connector |
| Difficulty | Intermediate |
| Time required | 45–60 minutes |
| Last updated | July 2026 |
Building a Bubble-powered Jira issue portal
The most popular Bubble + Jira integration is a customer-facing issue portal: clients submit bug reports or feature requests through a polished Bubble form, and the workflow automatically creates a Jira issue in the correct project with the right issue type and priority. Support teams and product managers then work entirely in Jira, while customers interact with the Bubble UI. The critical technical differentiator between Jira and other project management APIs: Jira Cloud's v3 REST API uses HTTP Basic Auth (not Bearer token), requires the authorization string to be Base64-encoded before pasting into Bubble's header, and uses Atlassian Document Format (ADF) — a structured JSON tree — for the description field. Sending a plain string for description causes HTTP 400 on every create attempt.
Integration method
Call the Jira Cloud REST API v3 from Bubble's server-side API Connector using HTTP Basic Auth encoded as a Private Authorization header.
Prerequisites
- A Jira Cloud account (the Free plan supports API access) and at least one Jira project to test with
- Your Atlassian account email address and a Jira API token (generated at id.atlassian.com/manage-profile/security/api-tokens)
- Your Jira subdomain (the part before .atlassian.net in your Jira URL, e.g., mycompany in mycompany.atlassian.net)
- The API Connector plugin installed in your Bubble app (Plugins tab → Add plugins → search 'API Connector' by Bubble)
- A free Base64 encoder tool in your browser (e.g., base64encode.org) to encode the auth string
Step-by-step guide
Generate your Jira API token and prepare the Basic Auth string
Open your browser and go to id.atlassian.com/manage-profile/security/api-tokens. Click 'Create API token', give it a label like 'Bubble Integration', and click Create. Copy the token value immediately — it is only shown once. Now you need to create the Basic Auth string. Jira uses HTTP Basic Auth where the username is your Atlassian account email and the password is the API token. Concatenate them with a colon: youremail@company.com:your_api_token_here. Then Base64-encode this entire string. You can do this at base64encode.org — paste the email:token string into the encoder and copy the resulting Base64 string. The final Authorization header value will be: Basic YOUR_BASE64_STRING (the word 'Basic' followed by a space, then the encoded string). Store this complete header value somewhere safe — you will paste it into Bubble's API Connector in the next step.
1Step-by-step auth string construction:231. Your email: youremail@company.com42. Your API token: your_api_token_here53. Concatenate: youremail@company.com:your_api_token_here64. Base64-encode the result: eW91cmVtYWlsQGNvbXBhbnkuY29tOnlvdXJfYXBpX3Rva2VuX2hlcmU=75. Final header value: Basic eW91cmVtYWlsQGNvbXBhbnkuY29tOnlvdXJfYXBpX3Rva2VuX2hlcmU=Pro tip: If you change your Atlassian email or revoke the API token, the Base64 string becomes invalid and you must regenerate and update it manually in Bubble's API Connector — it is not auto-refreshed like OAuth.
Expected result: You have a complete Basic auth header value in the format 'Basic [base64string]' ready to paste into Bubble.
Configure the API Connector with the Jira base URL and Private auth header
In Bubble, go to Plugins → API Connector → click 'Add another API'. Name the group 'Jira API'. The Jira Cloud API base URL is specific to your organization's subdomain. In the API base URL field, enter: https://YOUR-DOMAIN.atlassian.net/rest/api/3 — replace YOUR-DOMAIN with your actual Jira subdomain. This is important: every user's Jira instance is at a different subdomain, so the URL is not generic. Under 'Shared headers for all calls', add one header: key = Authorization, value = Basic YOUR_BASE64_STRING (paste the full value from Step 1). Check the Private checkbox on this header — this keeps the Base64-encoded credentials off the client side. Also add a Content-Type header: key = Content-Type, value = application/json (this is needed for POST calls). Leave Content-Type as non-Private (it is not a secret). You now have two shared headers: Authorization (Private) and Content-Type.
1API Connector Group: Jira API2 Base URL: https://YOUR-DOMAIN.atlassian.net/rest/api/334 Shared headers:5 - Authorization: Basic <base64(email:token)> [Private: YES]6 - Content-Type: application/json [Private: NO]Pro tip: The base URL includes the subdomain — if your team has multiple Atlassian domains (e.g., separate Jira instances for different organizations), you will need separate API Connector groups per subdomain.
Expected result: The Jira API group in your API Connector shows the subdomain-specific base URL and two shared headers: Authorization (Private) and Content-Type.
Add a JQL search call to read Jira issues
In the Jira API group, click 'Add another call'. Name it 'Search Issues'. Set Method to POST and URL to /search (the full URL will be your base URL + /search = https://YOUR-DOMAIN.atlassian.net/rest/api/3/search). Set 'Use as' to 'Data' so Bubble treats this as a data source for display elements. In the Body, check 'Send arbitrary data' and provide a JSON body with a JQL query string and the fields you want returned. JQL (Jira Query Language) is a SQL-like syntax for filtering issues. A basic example: query all open issues in a project ordered by creation date. Click 'Initialize call' — Bubble will send the request to Jira and parse the schema. After initialization, Bubble detects the 'issues' array with nested fields like issues[0].key, issues[0].fields.summary, issues[0].fields.status.name, and issues[0].fields.assignee.displayName. You can bind these to a Repeating Group's data source.
1POST /search23Body:4{5 "jql": "project = YOUR_PROJECT_KEY AND status != Done ORDER BY created DESC",6 "maxResults": 50,7 "fields": [8 "summary",9 "status",10 "priority",11 "assignee",12 "created",13 "issuetype"14 ]15}Pro tip: Replace YOUR_PROJECT_KEY with the actual Jira project key — visible in parentheses next to the project name in Jira (e.g., SUPPORT, ROADMAP, IT). The key is case-sensitive in some contexts.
Expected result: Initialize call succeeds and Bubble detects the 'issues' array with fields like summary, status.name, and priority.name. You can now bind this call to a Repeating Group.
Add an issue creation call using Atlassian Document Format
In the Jira API group, click 'Add another call'. Name it 'Create Issue'. Set Method to POST and URL to /issue. Set 'Use as' to 'Action' since this is triggered from a workflow. In the Body, check 'Send arbitrary data'. The most critical part of this call is the description field structure — Jira Cloud v3 does NOT accept a plain string. It requires Atlassian Document Format (ADF): a JSON tree with a doc node containing content blocks. Sending 'description': 'my bug description' returns HTTP 400 Bad Request immediately. Use the ADF structure shown in the code example below. Define dynamic parameters for summary (<issue_summary>), description content (<issue_description>), and project key (configure as a static value matching your project). Initialize the call with real example values to confirm the structure. After initialization, Bubble confirms the call works and detects the returned issue id and key.
1POST /issue23Body:4{5 "fields": {6 "project": {7 "key": "YOUR_PROJECT_KEY"8 },9 "summary": "<dynamic: issue_summary>",10 "issuetype": {11 "name": "Bug"12 },13 "priority": {14 "name": "Medium"15 },16 "description": {17 "type": "doc",18 "version": 1,19 "content": [20 {21 "type": "paragraph",22 "content": [23 {24 "type": "text",25 "text": "<dynamic: issue_description>"26 }27 ]28 }29 ]30 }31 }32}Pro tip: The issuetype name must match exactly what exists in your Jira project. Common types: 'Bug', 'Story', 'Task', 'Epic', 'Service Request'. If the name doesn't match, Jira returns 400 'Issue type is required'.
Expected result: The Initialize call creates a test issue in your Jira project and returns an issue id and key (like 'PROJ-42'). The issue appears in Jira's issue list with the correct summary and description.
Wire the Create Issue call to a Bubble form workflow
Now connect the Jira API calls to your Bubble UI. Add a form to your Bubble page with input elements for Issue Summary (single-line text input) and Description (multiline text input). Add a 'Submit' button. In the button's workflow: Step 1 — add a Plugin action 'Jira API — Create Issue'. Map the issue_summary parameter to the Summary input's value, and issue_description to the Description input's value. Step 2 (optional) — Make changes to a Thing: create a record in your Bubble database to store the returned issue key (from Step 1's result's 'key' field), the user who submitted it, and the submission timestamp. Step 3 (optional) — Show an element (e.g., a success popup) that displays 'Your ticket was created: ' + Step 1's result's key. For apps that need the Create Issue call to run server-side (e.g., to prevent token exposure in browser DevTools for public-facing apps), move this to a Backend Workflow — which requires a paid Bubble plan. Note: if you see 'This action will not run' in Bubble's workflow debugger, check that all required parameters have values and are not empty.
1Bubble Workflow: Button 'Submit Bug Report' is clicked23Step 1: [API Action] Jira API - Create Issue4 → issue_summary: Input Summary's value5 → issue_description: Input Description's value67Step 2: Create a new Thing (Bug Report)8 → jira_key: Result of Step 1's key9 → submitted_by: Current User10 → submitted_at: Current date/time11 → summary: Input Summary's value1213Step 3: Show element (Popup 'Success')14Step 4: Reset inputsPro tip: Save the Jira issue key returned in Step 1's result — it is the only way to link a Bubble record back to a specific Jira issue for future lookups or status updates.
Expected result: Filling the form and clicking Submit creates a Jira issue and displays the new issue key (e.g., PROJ-123) to the user. The issue appears in Jira within seconds.
Add issue status transitions (optional but commonly needed)
To allow Bubble workflows to move Jira issues between statuses (e.g., from 'To Do' to 'In Progress' or 'Done'), you need a two-step process. First, fetch the available transitions for an issue: add a GET call to /issue/{issue_key}/transitions — set 'Use as' to 'Data' and configure issue_key as a dynamic parameter. Initialize with a real issue key to detect the transitions array with id and name fields. Second, apply a transition: add a POST call to /issue/{issue_key}/transitions with a body of {"transition": {"id": "<transition_id>"}}. To use this in a Bubble workflow: Step 1 — call Get Transitions passing the issue key, Step 2 — call the POST transitions endpoint with the id from Step 1's result that matches the target status name. Transition IDs differ between Jira projects and workflow configurations — they are not universal numbers. RapidDev's team has configured Jira status transitions in Bubble for dozens of custom portals — reach out at rapidevelopers.com/contact for help with complex workflow configurations.
1Step A: GET /issue/<issue_key>/transitions2Returns:3{4 "transitions": [5 { "id": "11", "name": "To Do" },6 { "id": "21", "name": "In Progress" },7 { "id": "31", "name": "Done" }8 ]9}1011Step B: POST /issue/<issue_key>/transitions12Body:13{14 "transition": {15 "id": "21"16 }17}Pro tip: Transition IDs are specific to each Jira project's workflow configuration. Always call the GET /transitions endpoint first to discover the correct IDs for your project — never hardcode IDs from tutorials or examples.
Expected result: After applying the transition, the Jira issue moves to the new status. You can confirm by calling GET /issue/{key} and checking the fields.status.name value.
Common use cases
Customer bug report portal
External users submit bug reports through a branded Bubble form. The form collects a summary, description, severity level, and optional screenshot URL. A Bubble workflow creates a Jira issue in the appropriate project and returns the issue key (e.g., PROJ-123) to the user as a reference number.
When a user submits a bug report, create a Jira issue in project 'SUPPORT' with issue type 'Bug', populate the summary from the form title field, and include steps to reproduce in the description as ADF paragraph content.
Copy this prompt to try it in Bubble
Feature request tracking dashboard
A Bubble app displays a live list of feature requests by querying Jira with JQL filters. Users can vote or add comments, and the portal shows status updates as tickets move through Jira's workflow — all without users needing Jira logins.
Display a Repeating Group of open feature requests from Jira project 'ROADMAP' where status is not 'Done', sorted by creation date descending, showing issue key, summary, and status for each.
Copy this prompt to try it in Bubble
Internal ops request workflow
Internal team members submit IT requests, HR tickets, or procurement approvals via a custom Bubble form that creates structured Jira issues. Managers in Jira triage and approve requests; the Bubble app displays the current status back to the requester.
Build an IT request form in Bubble that creates a Jira issue in the 'IT-REQUESTS' project with type 'Service Request' and maps the request category dropdown to a Jira label.
Copy this prompt to try it in Bubble
Troubleshooting
Every POST call to Jira returns HTTP 400 Bad Request
Cause: The most common cause is the description field being sent as a plain string instead of Atlassian Document Format (ADF). Jira Cloud v3 rejects any description that is not a properly structured ADF JSON object.
Solution: Update the description field in your API call body to use the ADF structure: { "type": "doc", "version": 1, "content": [{ "type": "paragraph", "content": [{ "type": "text", "text": "your description here" }] }] }. Re-initialize the call after updating the body structure.
1"description": {2 "type": "doc",3 "version": 1,4 "content": [5 {6 "type": "paragraph",7 "content": [8 { "type": "text", "text": "<dynamic: issue_description>" }9 ]10 }11 ]12}API calls return HTTP 401 Unauthorized
Cause: The Basic Auth string is incorrect — the most common mistakes are: missing 'Basic ' prefix before the encoded string, the email or token changed after the string was generated, or the Base64 encoding was done incorrectly.
Solution: Regenerate the Base64 string: concatenate email:token exactly (no spaces around the colon), re-encode at base64encode.org, and update the Authorization header in Bubble's API Connector to 'Basic ' + the new encoded string. Make sure the Private checkbox is still checked after updating.
'There was an issue setting up your call' when initializing — no response detected
Cause: The Initialize call needs a real successful response to detect the schema. This fails if the Jira project key in the JQL query or issue creation body does not exist, or if the API URL has a typo.
Solution: Verify the Jira project key by checking your Jira board — it is the uppercase abbreviation shown in issue numbers (e.g., PROJ in PROJ-1). Confirm the base URL has the correct subdomain. Try the URL in your browser while logged into Jira to confirm it responds.
Search call returns issues but status or assignee fields show as empty in Bubble
Cause: Some Jira fields (like assignee) can be null for unassigned issues. Bubble treats a null nested object as an empty value and may show blank rather than a default label.
Solution: In your Bubble Repeating Group, use conditional logic: if Current cell's issues's assignee displayName is empty, display 'Unassigned'; otherwise display the assignee name. This prevents blank cells for issues that haven't been assigned.
Create Issue succeeds for me but fails when another user submits the form in the app
Cause: The API call may be executing client-side, exposing the Basic Auth header in the browser's network tab. Public-facing Bubble apps on the Free plan cannot reliably protect API credentials from client-side inspection.
Solution: Upgrade to a paid Bubble plan and move the Create Issue call to a Backend Workflow. Backend Workflows run entirely on Bubble's servers, ensuring the Basic Auth header never appears in the user's browser network requests.
Best practices
- Always mark the Authorization header as Private in Bubble's API Connector — the Base64-encoded Basic Auth string is just as sensitive as a plaintext password, and exposing it allows anyone to create, modify, or delete Jira issues as your account.
- Add Privacy rules in Bubble's Data tab for any Things that store Jira issue keys or user data — without Privacy rules, all records are accessible to any logged-in Bubble user via Bubble's data API.
- Use a dedicated Jira service account or API token for the Bubble integration rather than your personal Jira account token — this prevents disruption to your Jira access if the integration needs to be revoked or rotated.
- Limit the fields returned by JQL search queries using the 'fields' array parameter — requesting only the fields you need (summary, status, priority) rather than all fields reduces the response payload and Bubble WU consumption.
- Store the Jira issue key returned by the Create Issue call in Bubble's database — this key is the permanent reference that links a Bubble record to a Jira issue for future status lookups or updates.
- For public-facing Bubble apps where anyone can submit a form, use a Backend Workflow (paid plan) to run the Jira API call server-side — prevents the Basic Auth credentials from appearing in the browser's network inspector.
- Handle the case where issue fields like assignee or priority are null in Bubble's UI with conditionals — Jira returns null for these fields on unassigned or unprioritized issues, causing blank cells in Repeating Groups.
Alternatives
ClickUp uses a simpler personal token in the Authorization header (no Base64 encoding, no Bearer prefix), a standard REST v2 API (no ADF for descriptions), and millisecond timestamps. Jira is targeted at software engineering teams with sprints and issue types; ClickUp is broader for cross-functional operations and product teams. Choose Jira if your engineering team already uses it; choose ClickUp for a simpler initial setup.
Notion uses a simpler Bearer token (no Base64 encoding) and is primarily used as a content CMS or lightweight database in Bubble, not for structured project management with workflows. Use Jira for engineering sprint tracking and bug management; use Notion for content management and team wikis.
Monday.com uses a GraphQL API (all operations via a single POST endpoint) with a Bearer token — simpler auth than Jira's Base64 Basic Auth, but requires building GraphQL query strings in Bubble's API Connector body. Monday.com is better for visual project tracking across non-engineering teams; Jira has deeper software development workflow support.
Frequently asked questions
Does connecting Bubble to Jira require a paid Jira plan?
No. Jira Cloud's Free plan includes API access. You can create an API token and make REST API calls with a free Jira account supporting up to 10 users. However, some Jira features (like advanced workflow automation or certain issue types) may require paid plans. Check Jira's current pricing for specific feature availability.
What is Atlassian Document Format (ADF) and why does Jira require it?
ADF is Atlassian's structured JSON format for rich text content. Jira Cloud v3 uses it for all text fields that support formatting (bold, bullet lists, code blocks, links). A plain string like 'my description' is rejected with HTTP 400. The minimum valid ADF structure for a plain paragraph is: { "type": "doc", "version": 1, "content": [{ "type": "paragraph", "content": [{ "type": "text", "text": "your text here" }] }] }.
Can I attach files or screenshots to Jira issues from Bubble?
Yes, but it requires a separate API call. The Jira attachment endpoint accepts multipart/form-data POST requests to /issue/{issueIdOrKey}/attachments. This is more complex to configure in Bubble's API Connector because it requires multipart encoding. The typical Bubble approach is to store files in Bubble's file storage or AWS S3 and include the file URL as text in the issue description rather than attaching the binary file.
Why do I need to Base64-encode the auth string instead of just using the token directly?
HTTP Basic Authentication is a standard protocol that requires the credentials to be Base64-encoded in the Authorization header. Jira Cloud mandates Basic Auth for API token authentication — it does not support using the API token as a Bearer token directly. The Base64 encoding is not encryption; it is just encoding. That is why marking the header as Private in Bubble is critical — without Private, the encoded string is sent to the browser and can be easily decoded.
How do I get a list of all available issue types and priorities for my Jira project?
Add two GET calls to your API Connector: GET /project/YOUR_PROJECT_KEY/statuses returns available issue types with their statuses. GET /priority returns all priority levels. Initialize each call with your project key to see the available options. This is useful for populating Bubble dropdowns that let users select the issue type or priority when submitting a form.
Talk to an Expert
Our team has built 600+ apps. Get personalized help with your project.
Book a free consultation