ml-connector
XeroJira

Xero and Jira integration

Xero runs your accounting and financial close. Jira tracks your project work, issues, and team capacity. Connecting them bridges the gap between project management and cost accounting: team members tag issues with cost codes tied to Xero projects and cost centers, and ml-connector flows those assignments and estimates back into Xero as tracking dimensions on manual journals. Issue changes sync in real time, and your project spend visibility is no longer hidden in spreadsheets.

How Xero works

Xero is a cloud accounting platform exposing invoices, purchase orders, contacts, accounts, manual journals, and tracking categories through REST APIs at https://api.xero.com/api.xro/2.0/. Authentication uses OAuth2 Authorization Code flow with 30-minute access tokens and 60-day refresh tokens. Xero requires the Xero-tenant-id header on every request to target the specific organization. Data is available via webhooks for real-time CREATE and UPDATE events on invoices, payments, manual journals, and contacts, and via If-Modified-Since header for delta polling. Webhooks deliver metadata only, requiring a follow-up GET to fetch the full record. Rate limits are 5 concurrent calls and 60 per minute per tenant.

How Jira works

Jira is an issue tracking and project management platform exposing issues, projects, users, and worklogs through REST APIs at https://api.atlassian.com/ex/jira/{cloudId}/rest/api/3/. Authentication uses OAuth2 authorization code flow or Basic auth with email and API token. Jira publishes webhooks for issue lifecycle events including jira:issue_created, jira:issue_updated, and jira:issue_deleted, registered at POST /rest/api/3/webhook with a 30-day expiry that requires refresh before it expires. Webhook signatures verify via HMAC-SHA256 in the X-Hub-Signature header. Custom fields are per-instance and vary by Jira deployment. Jira has no native invoices, purchase orders, or accounting entities, so integration focuses on issue metadata and custom field enrichment.

What moves between them

The integration runs in two directions. From Jira to Xero: issue assignments, custom fields tagged with cost codes, and worklog hours are synced into Xero tracking categories and manual journal line items on a schedule tied to your weekly or monthly close. From Xero to Jira: tracking categories, cost codes, and project cost centers are synced to Jira custom fields so team members can tag issues with valid cost codes at creation time. Manual journals created from issue data carry the full issue key, project name, and assignee for reconciliation. Xero webhooks alert ml-connector to contact or tracking category changes, while Jira webhooks trigger on every issue update, ensuring both systems stay in sync.

How ml-connector handles it

ml-connector bridges the OAuth2 authorization flows for both systems independently, storing Xero and Jira credentials encrypted and refreshing each system's access tokens when a call returns 401. On the Jira side, it periodically calls PUT /rest/api/3/webhook/refresh to keep webhook registrations alive past their 30-day expiry, since an expired webhook silently stops firing. Custom fields in Jira are per-instance, so ml-connector queries the Jira instance at setup to discover the internal field IDs for cost-code and project-code fields, then maps those to Xero tracking categories by name. When an issue is created or updated in Jira with a cost-code custom field, ml-connector reads the change via webhook, looks up the matching Xero tracking category and cost code, and creates or updates a manual journal line in Xero with that tracking dimension. Xero's rate limit is 60 calls per minute per tenant and 5 concurrent calls, so ml-connector queues journal writes and batches them. Every record carries the issue key for reverse lookup and a full audit trail of the mapping decision. On the Xero side, new or changed tracking categories trigger a Xero webhook; ml-connector reads those, discovers the corresponding custom fields in Jira, and syncs them as options in the Jira custom field, so team members always see a current list of valid cost codes when tagging an issue.

A real-world example

A professional services firm runs Xero for accounting and close, and Jira for project tracking and delivery. Before the integration, project managers tracked staff hours and cost allocations in Jira issues and custom fields, but the finance team had to manually export issue assignments weekly and re-enter them into Xero's tracking categories as manual journals for project cost accounting. Month-end close was delayed while the finance team chased which issues were charged to which cost centers. With Xero and Jira connected, issue cost codes flow into Xero automatically, manual journals are created with full traceability to the issue, and the accounting team starts close with project cost data already reconciled to the billing system.

What you can do

  • Sync Jira issue cost-code custom fields into Xero tracking categories and manual journal lines for project cost accounting.
  • Keep Jira custom field options in sync with Xero cost codes and tracking categories so team members always tag with valid codes.
  • Map Jira project teams and issue assignments to Xero contact groups and cost centers for visibility across both systems.
  • Refresh Jira webhook registrations before their 30-day expiry, and renew OAuth2 tokens for both systems transparently.
  • Track every issue-to-journal mapping with full audit trail for reconciliation and replay if a downstream post fails.

Questions

What financial data actually moves between Xero and Jira?
Only metadata and references move. Xero cost codes and tracking categories sync to Jira custom field options so team members can tag issues with valid codes. When an issue is tagged with a cost code custom field, ml-connector reads that tag and creates a Xero manual journal line with the matching tracking dimension. Xero invoices and GL accounts themselves never appear in Jira; Jira is read for its issue and custom field data only.
Why do Jira webhooks need to be refreshed?
Jira webhooks automatically expire after 30 days and stop firing if not refreshed. ml-connector monitors webhook age and calls PUT /rest/api/3/webhook/refresh before expiry so issue updates continue to trigger syncs without manual intervention or silent data loss.
How does the integration handle per-instance Jira custom fields?
Custom field IDs are unique to each Jira instance. At setup, ml-connector queries the Jira instance to discover the internal IDs for fields like cost-code and project-code, then uses those IDs on every subsequent read and write. If you rename or delete a custom field in Jira, the mapping must be updated in ml-connector or the sync will fail.

Related integrations

Connect Xero and Jira

Free to use. Add your credentials, ping your real systems, and see if we fit.

Get started