Business Sign-off & Approval for Jira Cloud
Business Sign-off & Approval for Jira Cloud is built on the Atlassian Forge platform and runs entirely within Atlassian’s cloud infrastructure. The app does not transmit any data to Cahaba Forge LLC, third-party services, or any external endpoint. All data is stored using Atlassian Forge Storage and Jira Cloud APIs, and resides within Atlassian’s infrastructure in the same region as your Jira Cloud site.
In the event of any conflict between this Privacy Policy and the Cahaba Forge Data Processing Addendum (DPA) with respect to the processing of Personal Data on behalf of a Licensee, the DPA shall control.
Business Sign-off reads and writes data exclusively within Atlassian’s platform. It does not collect, transmit, or store data outside Atlassian’s infrastructure.
The app reads the following Jira data during normal operation. This data is accessed via Jira Cloud REST APIs and is never transmitted outside the Atlassian platform:
Note on email addresses: The app reads email addresses from the Jira user API at runtime for display purposes in the issue panel. Email addresses are not persisted in Forge Storage or any app-managed data store. They are transient — fetched on demand and discarded after the response is sent.
The app stores data using Atlassian Forge Storage (a managed key-value store provided by Atlassian) and Jira issue properties. All storage keys are prefixed with bsf: for Forge Storage or bso. for Jira issue properties.
One record per approver assignment on each issue.
| Field | Data | Purpose |
|---|---|---|
| issueId | Jira issue internal ID | Links approver to issue |
| accountId | Atlassian account ID (opaque identifier) | Identifies the approver |
| status | ADDED, PENDING, APPROVED, RETURNED | Tracks decision state |
| decisionComment | Free-text (up to 450 characters) | Approver’s decision comment |
| createdDate | ISO timestamp | When the approver was assigned |
| createdByAccountId | Atlassian account ID | Who assigned the approver |
| decisionDate | ISO timestamp | When the decision was made |
| lastNotifiedDate | ISO timestamp | When the approver was last emailed |
| decisionAtStatusId | Jira status ID | Issue status at time of decision (for decision locking) |
| sodViolation | Boolean | Whether a Separation of Duties conflict exists |
| sodReason | Text | Reason for SoD conflict (e.g., “assignee”) |
| pseudonymizedAt | ISO timestamp (optional) | When the record was pseudonymized for GDPR compliance |
Append-only audit trail records for compliance. Each record captures a point-in-time snapshot of the action context and is integrity-protected with a SHA-256 hash.
| Field | Data | Purpose |
|---|---|---|
| id | UUID | Unique record identifier |
| issueId, projectKey, issueKey | Issue identifiers | Links record to issue |
| issueSummary, issueType, issuePriority, issueStatus | Issue snapshot | Captures issue state at action time |
| action | Action type (e.g., DECISION_APPROVED, APPROVER_ADDED) | What happened |
| timestamp | ISO timestamp | When it happened |
| actorAccountId, actorDisplayName | Actor snapshot | Who performed the action |
| targetAccountId, targetDisplayName | Target snapshot | The approver acted upon |
| reporterAccountId, reporterDisplayName | Reporter snapshot | Issue reporter at action time |
| assigneeAccountId, assigneeDisplayName | Assignee snapshot | Issue assignee at action time |
| previousApprovalStatus, newApprovalStatus | State change | Before/after values |
| source | UI, WORKFLOW_POST_FUNCTION, EVENT_TRIGGER | How the action was initiated |
| sodResult | PASS, FAIL, NOT_APPLICABLE | SoD validation outcome |
| comment | Decision comment | Text provided with the decision |
| integrityHash | SHA-256 hex string | Tamper-detection hash |
| pseudonymizedAt | ISO timestamp (optional) | When the record was pseudonymized for GDPR compliance |
Note on display names in audit records: The audit trail intentionally stores user display names as point-in-time snapshots. This is a compliance requirement — auditors need to see who acted and how they were identified at the time of the action, even if the user’s details later change. Email addresses are not stored in audit records. The Forge version uses Atlassian account IDs (opaque, non-PII identifiers) as the primary user reference.
Records administrative actions (configuration changes, plugin enable/disable) for accountability.
| Field | Data | Purpose |
|---|---|---|
| id | UUID | Unique record identifier |
| action | Action type (e.g., GLOBAL_CONFIG_CHANGED, PLUGIN_DISABLED) | What happened |
| timestamp | ISO timestamp | When it happened |
| actorAccountId | Atlassian account ID | Who performed the action |
| actorDisplayName | Display name snapshot | Actor identity at action time |
| source | UI, WORKFLOW_POST_FUNCTION, EVENT_TRIGGER | How the action was initiated |
| affectedObject, affectedObjectType | Text | What was changed |
| projectKey | Project key or null | For project-scoped changes |
| changedFields | Array of {field, fieldKey, from, to} | Configuration changes (old → new values) |
| details | Text or null | Additional context for the action |
| integrityHash | SHA-256 hex string | Tamper-detection hash |
| pseudonymizedAt | ISO timestamp (optional) | When the record was pseudonymized for GDPR compliance |
Plugin configuration settings. These contain no personal data — only feature toggles, thresholds, and permission rules.
Tracks background CSV export operations. Records are transient — those older than 72 hours are purged when the admin page is next accessed.
| Field | Data | Purpose |
|---|---|---|
| taskId | UUID | Task identifier |
| state | RUNNING, COMPLETED, FAILED | Task progress |
| progress | Number (0–100) | Completion percentage |
| progressMessage | Text or null | Current progress description |
| requestedByAccountId | Atlassian account ID | Who initiated the export |
| request | Export parameters (project keys, date range, decisions-only filter) | Query criteria |
| createdAt | ISO timestamp | When the export was initiated |
| completedAt | ISO timestamp or null | When the export finished |
| recordCount | Number or null | Total records exported |
| chunkCount | Number or null | Number of CSV data chunks |
| errorMessage | Text or null | Error details if the export failed |
| CSV data chunks | Serialized CSV (up to 30KB per chunk) | Export output |
The app creates two Jira custom fields that are visible on issues:
The app writes a single issue property (bso-signoff) containing approval statistics:
| Field | Data | Contains PII? |
|---|---|---|
| status | Approval status string | No |
| approvedCount, returnedCount, pendingCount, totalApprovers | Counts | No |
| percentage, threshold | Numbers | No |
| approvalRequired, pluginActive | Booleans | No |
| lastUpdated | ISO timestamp | No |
This property contains no personal data — only aggregate statistics. It enables JQL queries on approval status.
In addition to the bso-signoff statistics property, the app writes compliance-critical approval decision events to individual issue properties using the key pattern bso.approval.audit.NNNN (4-digit zero-padded sequence).
An additional index property (bso.approval.audit.index) tracks the current sequence number.
Each audit event property contains a JSON record with:
| Field | Data | Purpose |
|---|---|---|
| schemaVersion | Number | Property schema version for forward compatibility |
| sequence | Number | Zero-padded sequence within the issue |
| eventType | DECISION_APPROVED, DECISION_RETURNED, DECISION_CHANGED, DECISION_RESET, STATUS_CHANGED, RE_REVIEW_REQUESTED | What happened |
| eventSource | UI, WORKFLOW_POST_FUNCTION, EVENT_TRIGGER | How the action was initiated |
| eventTime | ISO timestamp | When it happened |
| issueContext | Issue ID, key, project key, summary, type, priority, status, reporter, assignee | Issue state snapshot at action time |
| actor | Account ID and display name | Who performed the action |
| target | Account ID and display name, or null | The approver acted upon |
| previousApprovalStatus, newApprovalStatus | State change | Before/after values |
| sodResult | PASS, FAIL, NOT_APPLICABLE, or null | SoD validation outcome |
| comment | Decision comment text or null | Text provided with the decision |
| recordHash | SHA-256 hex string | Hash of this event’s content |
| previousHash | SHA-256 hex string or null | Hash of the preceding event (chain integrity) |
| pseudonymizedAt | ISO timestamp (optional) | When the record was pseudonymized for GDPR compliance |
These properties are native Jira data and survive app uninstall. They are designed to provide a permanent, verifiable audit trail within the customer’s Jira instance.
A per-issue list of Atlassian account IDs (bsf:issue-approvers:{issueId}) used for efficient lookup. Contains the same account IDs as the individual approver records.
The app maintains a single record (bsf:known-accounts) listing every Atlassian account ID that appears anywhere in the app’s stored data, along with an ISO timestamp of the last time the app touched data for that account. This registry is used solely to feed Atlassian’s Personal Data Reporting API on a weekly basis, as required for Marketplace apps that store personal data. The registry contains only opaque Atlassian account IDs — no display names, email addresses, or other personal information. Account IDs are removed from the registry once the corresponding user has been closed by Atlassian and the app has completed pseudonymization for that account.
Licensing is managed entirely by Atlassian through the Jira Cloud Marketplace. The app does not store license keys, license validation data, or licensing-related personal data. The app checks license status at runtime via the Forge licensing API.
All data processing occurs within Atlassian’s platform for the following purposes:
/rest/api/3/issue/{key}/notify)pseudonymizedAt timestamp is added to each modified record to indicate when compliance action was taken.bsf:known-accounts registry once Atlassian reports the account as closed and pseudonymization for that account has completed. This prevents the app from continuing to report closed accounts in subsequent weekly Personal Data Reporting cycles.None outside the Atlassian platform. Business Sign-off does not transmit any data to Cahaba Forge LLC, or any third party. All processing occurs within Atlassian’s Forge platform and Jira Cloud.
Business Sign-off makes a single category of outbound call to an Atlassian-operated endpoint outside the Jira REST API: a weekly POST to https://api.atlassian.com/app/report-accounts/. This call submits the list of Atlassian account IDs the app has stored data for, so that Atlassian can route account lifecycle events (closure, profile updates) back to the app. This is an Atlassian Marketplace requirement for apps that store personal data. Only opaque Atlassian account IDs and ISO timestamps are transmitted — no display names, email addresses, comments, or other personal information.
Atlassian, as the infrastructure provider, has access to Forge Storage and Jira Cloud data in accordance with Atlassian’s own Privacy Policy and Cloud Terms of Service.
Cahaba Forge does not use Licensee data, Data Subject information, approval decisions, or audit records for training, tuning, fine-tuning, or evaluating any artificial intelligence or machine learning models. The App does not employ AI or ML in any form. Cahaba Forge does not make Licensee data available to any third party for AI or ML purposes.
Business Sign-off does not use cookies, web beacons, tracking pixels, browser fingerprinting, or any client-side tracking technologies. The app does not set or read any cookies in the user’s browser. The issue panel renders natively within Jira (UI Kit, no iframe). The admin and project settings pages render within a Forge-managed iframe and do not include any analytics, telemetry, or tracking scripts.
getUserDataReport resolver. The report returns counts (approver records, history entries, admin audit entries) and the list of issues the user has interacted with — never the underlying record contents or display names. Admins can use the existing audit CSV export to retrieve full content if needed.bso.approval.audit.NNNN): These properties are native Jira data and persist independently of the app — they survive app uninstall. They are deleted only when the parent Jira issue is deleted.bso.approval.audit.NNNN) persist after app uninstall as native Jira data and are deleted only when the parent issue is deleted. Note: Pseudonymization (rather than full deletion) of audit records is the standard approach for compliance tools where regulatory retention obligations may apply. Erasure of audit records may conflict with such obligations — consult your Data Protection Officer./app/report-accounts/ API. Atlassian uses this report to identify accounts that have been closed or updated, and the app responds by pseudonymizing data for closed accounts. This satisfies the Atlassian Marketplace requirement for apps that store personal data.a) Data Controller: You (the Licensee) are the data controller for all personal data processed by the app within your Jira Cloud site.
b) Data Processor / Sub-Processor: Cahaba Forge LLC is the data processor. The app runs on Atlassian’s Forge platform, and Cahaba Forge LLC does not directly access, process, or store your data — the app code runs within Atlassian’s isolated Forge runtime. Atlassian acts as a sub-processor for infrastructure, hosting, and data storage.
c) Legal Basis for Processing: The legal basis for Cahaba Forge’s processing is performance of its contract with Licensee (GDPR Article 6(1)(b)) and compliance with its obligations as a processor under GDPR Article 28. The legal basis for the underlying processing of Data Subject information is determined by Licensee as Controller, typically legitimate interests in maintaining compliance audit trails and approval workflow integrity (Article 6(1)(f)), or legal obligation where compliance with laws such as SOX, SOC 2, or equivalent frameworks is applicable (Article 6(1)(c)). Licensee is responsible for ensuring an appropriate legal basis exists for its processing activities.
d) Personal Data Processed: The app processes Atlassian account IDs and display names as part of approver management and audit trail recording. Email addresses are read at runtime for UI display but are not persisted by the app.
e) Data Retention: Data is stored in Forge Storage for the lifetime of the associated Jira issue (or app installation for configuration data) and is automatically deleted when the issue is deleted or the app is uninstalled.
f) Data Subject Rights: You are responsible for fulfilling data subject requests (access, erasure, rectification, portability) using the app’s audit export, issue deletion, and app uninstall capabilities.
g) Sub-Processors: Atlassian Pty Ltd (Forge platform and Jira Cloud infrastructure). No other sub-processors.
h) International Transfers: Data resides in Atlassian’s infrastructure, subject to Atlassian’s data residency and international transfer policies.
A Data Processing Addendum (DPA) is available for customers requiring GDPR compliance documentation. To request a signed copy, contact privacy@cahabaforge.com.
For Licensees and Data Subjects subject to the California Consumer Privacy Act of 2018 as amended (“CCPA”):
For detailed CCPA terms, see the Cahaba Forge Data Processing Addendum.
The app includes a diagnostic logging toggle on the global configuration page. When enabled by an administrator, the app emits detailed trace logs to the Forge runtime. These logs:
Standard operational logs (errors, warnings) are always active and may include account IDs and issue keys for debugging purposes. These logs are managed by the Forge platform and are subject to Atlassian’s log retention policies.
When the weekly Personal Data Reporting trigger pseudonymizes data for a closed user account, the app emits operational log lines summarizing the number of records modified per storage location and any errors encountered. These logs include the affected Atlassian account ID (which is already pseudonymous because the account has been closed) but never include display names, email addresses, or decision comment text.
Business Sign-off is enterprise software designed for use in organizational settings. It is not intended for use by individuals under the age of 16 (or such younger age as permitted by applicable law). The app does not knowingly collect data from children.
We may update this privacy policy to reflect changes in the app’s data handling practices. Material changes will be communicated with at least thirty (30) days’ prior notice through the Cahaba Forge website and/or email notification. The “Last Updated” date at the top of this document indicates when the policy was last revised.
For privacy-related questions or concerns:
This Privacy Policy primarily covers Personal Data processed by the App on behalf of Licensees. Separately, Cahaba Forge acts as a controller for a limited category of data collected directly in the course of its business:
Cahaba Forge does not sell, rent, or share this directly-collected data with third parties except as necessary to operate its business (for example, email service providers used to deliver correspondence) or as required by law.
This privacy policy applies to Business Sign-off & Approval for Jira Cloud. The app runs on Atlassian’s Forge platform — all data processing occurs within Atlassian’s infrastructure. Cahaba Forge LLC does not have direct access to your Jira instance or its data.