Jira is the primary task management system for dev and CS teams in most B2B SaaS companies. Without Kommo integration, after Won the manager manually creates a Jira ticket, specifying client type, plan, and technical context. When the ticket moves to Done, Kommo does not know. The Jira REST API connection closes this cycle: Won -> Jira Task -> Done -> Note in the deal.
Why the native integration does not close the gap
The Kommo Marketplace offers several Jira widgets, but most are simply a link to Jira from the deal card. There is no two-way synchronization: a task status change in Jira does not update the deal in Kommo. Creating a ticket with data from custom deal fields (plan, technical stack, onboarding requirements) via these widgets is impossible.
Zapier supports creating a Jira task on Kommo status change, but cannot: populate the ticket description from multiple custom fields, handle a reverse Jira webhook, or work with Jira custom fields (Story Points, priority, epic).
Jira REST API v3 gives full control: task creation with any data structure, transition management, comment reading — all via a single API key.
What gets synchronized
Kommo -> Jira:
— Deal name -> task Summary
— Plan and tier -> labels or custom field
— Contact name and email -> task Description
— Technical stack from custom field -> Components
— Kommo deal ID -> custom field kommo_deal_id for tracing
Jira -> Kommo:
— Task moved to Done -> Note in Kommo: “Onboarding complete”
— Jira comment from tech team -> Note in deal
— Task blocked -> task for manager: clarify with client
Architecture
Kommo Webhook: deal moved to Won
↓ Backend
1. GET /api/v4/leads/{id} + contacts
-> client name, email, plan, technical stack
2. Jira: POST /rest/api/3/issue
-> project: {key: 'OB'}, issuetype: {name: 'Task'}
-> summary, description with client data
-> labels: ['onboarding', plan]
-> customfield_10001: deal_id (Kommo Deal ID)
-> get issue_key (OB-123)
3. Kommo: PATCH /leads/{deal_id}
-> jira_ticket = 'OB-123'
-> jira_ticket_url = 'https://company.atlassian.net/browse/OB-123'
4. Kommo: POST /leads/{deal_id}/notes
-> "Jira ticket OB-123 created: https://..."
Jira Webhook: issue_updated (transition to Done)
↓ Backend
1. From payload: issue.key, changelog.items (status change)
2. Check: to.name == 'Done'
3. Find deal_id from customfield_10001 (kommo_deal_id)
4. Kommo: POST /leads/{deal_id}/notes
-> "Jira OB-123: task closed. Onboarding complete."
5. Optional: PATCH /leads/{deal_id} -> jira_status = done
Jira REST API v3: key requests
Base URL: https://{site}.atlassian.net/rest/api/3/. Authentication: Basic Auth, username = email, password = api_token (create at atlassian.com/manage-profile/security/api-tokens).
Create a task:
import requests
from requests.auth import HTTPBasicAuth
JIRA_URL = 'https://yourcompany.atlassian.net'
JIRA_EMAIL = 'bot@yourcompany.com'
JIRA_TOKEN = 'your_api_token'
JIRA_AUTH = HTTPBasicAuth(JIRA_EMAIL, JIRA_TOKEN)
def create_jira_issue(summary: str, description: str,
project_key: str, labels: list,
kommo_deal_id: int) -> str:
payload = {
'fields': {
'project': {'key': project_key},
'issuetype': {'name': 'Task'},
'summary': summary,
'description': {
'type': 'doc',
'version': 1,
'content': [{
'type': 'paragraph',
'content': [{'type': 'text', 'text': description}]
}]
},
'labels': labels,
'priority': {'name': 'Medium'},
# Custom field - find ID via GET /rest/api/3/field
'customfield_10200': str(kommo_deal_id) # Kommo Deal ID
}
}
resp = requests.post(
f'{JIRA_URL}/rest/api/3/issue',
auth=JIRA_AUTH,
json=payload
)
resp.raise_for_status()
return resp.json()['key'] # 'OB-123'
Task description from Kommo data:
def build_issue_description(lead: dict, contact: dict) -> str:
plan = get_custom_field(lead, PLAN_FIELD_ID)
tech_stack = get_custom_field(lead, TECH_STACK_FIELD_ID)
deal_amount = lead.get('price', 0)
return (
f'Client: {contact.get("name")}\n'
f'Email: {get_contact_email(contact)}\n'
f'Plan: {plan}\n'
f'Deal amount: ${deal_amount}\n'
f'Technical stack: {tech_stack}\n'
f'Kommo Deal ID: {lead["id"]}'
)
Handling Jira Webhook:
from flask import Flask, request
app = Flask(__name__)
@app.route('/webhooks/jira', methods=['POST'])
def jira_webhook():
payload = request.json
event = payload.get('webhookEvent')
if event != 'jira:issue_updated':
return '', 200
issue = payload.get('issue', {})
issue_key = issue.get('key')
changelog = payload.get('changelog', {})
# Look for status change in changelog
status_change = None
for item in changelog.get('items', []):
if item.get('field') == 'status':
status_change = item
break
if not status_change:
return '', 200
to_status = status_change.get('toString', '')
if to_status != 'Done':
return '', 200
# Get kommo_deal_id from Jira custom field
custom_fields = issue.get('fields', {})
kommo_deal_id = custom_fields.get('customfield_10200')
if kommo_deal_id:
deal_id = int(kommo_deal_id)
create_kommo_note(
deal_id,
f'Jira {issue_key}: task closed (Done). Onboarding complete.'
)
return '', 200
Get list of Jira custom field IDs:
def get_jira_fields():
resp = requests.get(
f'{JIRA_URL}/rest/api/3/field',
auth=JIRA_AUTH
)
return {f['name']: f['id'] for f in resp.json()}
# Run once to find required field IDs:
# fields = get_jira_fields()
# print(fields) # {'Story Points': 'customfield_10016', ...}
Registering a Jira Webhook:
Jira Cloud allows registering webhooks via Admin: Settings -> System -> Webhooks -> Create a WebHook. Specify the URL and select events. For Issue Updated, select: Issue updated with filter project = OB AND status = Done.
Registration via API is also available: POST /rest/webhooks/1.0/webhook (requires Jira Admin permissions).
Mapping Kommo plans to Jira labels
PLAN_TO_LABELS = {
'Starter': ['onboarding', 'tier-starter'],
'Pro': ['onboarding', 'tier-pro'],
'Enterprise': ['onboarding', 'tier-enterprise', 'priority-high'],
}
def get_jira_labels(kommo_plan: str) -> list:
return PLAN_TO_LABELS.get(kommo_plan, ['onboarding'])
Enterprise clients receive the priority-high label — the CS team sees them in a separate Jira filter.
Real-world case
B2B SaaS (EU, 30–40 new clients per quarter, Kommo + Jira + Notion, onboarding team of 4):
- Before: after Won, the manager spent 15–20 minutes creating a Jira ticket. Often forgot to specify the plan or technical stack — CS found out during the onboarding process. When the ticket was closed in Jira, the Kommo deal was left without an update.
- After: Won -> Jira Task in 40 seconds with a complete description from CRM. The CS team sees everything they need in the ticket. On Done -> automatic Note in Kommo. Sales Director sees onboarding status directly in the deal.
- Additionally: if a ticket sits without progress for 7 days — Jira automation (or scheduled webhook) -> task for manager to check.
Who this is relevant for
- B2B SaaS with a separate onboarding / Customer Success / Implementation team
- After Won, sales hands the client to CS via Jira tasks
- CS team does not work in CRM, Sales team does not work in Jira
- 15+ client handoffs per month — manual ticket creation is no longer cost-effective
Frequently asked questions
Jira API — OAuth or Basic Auth?
For server-side integrations without user involvement — Basic Auth with email and API Token. The API Token is created in the Atlassian profile (atlassian.com/manage-profile/security/api-tokens), not in Jira. OAuth 2.0 is needed for applications where the user authorizes access themselves.
How does the Jira Webhook verify that a request is genuine?
Jira Cloud does not support HMAC webhook signatures by default. Protection options: IP whitelist (if you have a static IP) or a secret parameter in the URL (Jira appends it to the request). Additionally: verify that the payload contains the expected project key.
How do I find the Jira custom field ID for the API?
Via GET /rest/api/3/field — returns all fields with their IDs. Custom fields have IDs in the format customfield_XXXXX. The ID is also visible in the URL when editing the field in Jira: Settings -> Issues -> Custom Fields -> Edit field.
What if one client has multiple deals in Kommo?
Store the kommo_deal_id of the specific Won deal in the Jira ticket. On the reverse webhook, use this ID to update the correct deal. If there are multiple deals — create a separate ticket for each Won deal.
Jira Server vs Jira Cloud — different APIs?
Jira Cloud uses REST API v3 (atlassian.net). Jira Server/Data Center — REST API v2 (your domain). Key difference: task description in Cloud requires Atlassian Document Format (JSON tree), in Server — plain text or wiki markup.
Summary
- Jira REST API v3: Basic Auth (email:api_token), Base URL
https://{site}.atlassian.net/rest/api/3/ - Create task:
POST /issuewith fields from Kommo (summary, description, labels, custom fields) - Webhook on Done -> Note in Kommo via
kommo_deal_idfrom Jira custom field - Task description: Atlassian Document Format (ADF) for Jira Cloud
- Typical development timeline — 1–2 weeks
If you have Kommo and Jira and client handoffs happen manually — describe your project and status structure. Exceltic.dev will configure ticket creation and reverse onboarding status synchronization.