Kommo + Jira: development team tasks from won deals
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.