A standalone Clojure SDK for the Atlassian Cloud REST APIs, built on
hato (an idiomatic wrapper over the JDK 11
java.net.http client — no Apache HttpClient on the classpath) and
cheshire for JSON.
Jira (575 fns) and Confluence (334 fns — both the modern v2 and the legacy v1 surfaces) are implemented comprehensively: every user-token-accessible endpoint, generated from the official OpenAPI specs and verified against them. Bitbucket is scaffolded for later.
src/atlassian/
config.clj env/.env resolution, auth header, URL bases (no network)
client.clj the ONLY namespace that touches the network (hato)
jira/
client.clj Jira client handle + the `request` seam
adf.clj Atlassian Document Format <-> plain text helpers
issues.clj one namespace per endpoint group...
search.clj ...search/JQL
comments.clj
attachments.clj
... (24 endpoint namespaces in total)
confluence/
client.clj Confluence client handle + `request` (:api :v2 default | :v1)
v2/ 12 namespaces over the modern /wiki/api/v2 surface
v1/ 10 namespaces over the legacy /wiki/rest/api surface (CQL search, etc.)
bitbucket/ (scaffolded)
(require '[atlassian.confluence.client :as cf]
'[atlassian.confluence.v2.pages :as pages]
'[atlassian.confluence.v1.search :as search])
(def c (cf/client)) ; same site + API token as Jira
;; v2 (modern, cursor-paginated)
(pages/get-pages c {:limit 25})
(pages/create-page c {:spaceId "123456" :title "From Clojure"
:body {:representation "storage" :value "<p>hi</p>"}})
;; page through cursors
(let [r (pages/get-pages c {:limit 25})]
(cf/next-cursor r)) ; -> cursor string for the next page
;; v1 CQL search (cql is a positional required arg)
(search/search-by-cql c "type=page and space=DEMO" {:limit 10})
The Confluence client routes on :api — :v2 (default, /wiki/api/v2) or :v1
(/wiki/rest/api). Namespaces are split by version to avoid collisions
(atlassian.confluence.v2.* vs .v1.*).
The endpoint namespaces were generated from the official Jira OpenAPI spec
(resources/openapi/jira-platform.v3.json, sliced per-group under
resources/openapi/groups/). Each function is a thin, typed call over
atlassian.jira.client/request.
Set these in a local, gitignored .env (or the process environment):
JIRA_BASE_URL=https://your-site.atlassian.net
JIRA_USER_EMAIL=you@example.com
JIRA_API_TOKEN=your-api-token # https://id.atlassian.com/manage-profile/security/api-tokens
Cloud uses Basic auth (base64(email:token)); Data Center PATs use Bearer
(JIRA_INSTANCE_TYPE=datacenter).
(require '[atlassian.jira.client :as jira]
'[atlassian.jira.issues :as issues]
'[atlassian.jira.search :as search]
'[atlassian.jira.adf :as adf])
(def c (jira/client)) ; resolves creds from env/.env
;; Read an issue
(issues/get-issue c "PROJ-123" {:fields "summary,status,assignee"})
;; Search (bounded JQL required by the enhanced /search/jql endpoint)
(search/search-jql c {:jql "project = PROJ order by created DESC" :maxResults 10})
;; Create an issue (description is ADF)
(issues/create-issue c
{:fields {:project {:key "PROJ"}
:issuetype {:name "Task"}
:summary "Created from the Clojure SDK"
:description (adf/text->adf "Hello from clojure-atlassian-sdk.")}})
Every function delegates to atlassian.jira.client/request, which returns parsed,
keywordized JSON on 2xx and throws ex-info {:status :method :url :body} on
errors (pass :throw? false in the request opts to get {:error ...} instead).
;; deps.edn
net.clojars.deadmeme5441/atlassian-sdk {:mvn/version "0.2.0"}
hato and cheshire come transitively. Build/publish locally with tools.build:
clojure -T:build jar # -> target/atlassian-sdk-<version>.jar (src only)
clojure -T:build install # install into local ~/.m2
clojure -T:build deploy # push to Clojars (needs CLOJARS_USERNAME / _PASSWORD)
The jar ships src only — the OpenAPI spec and per-group EDN under resources/
are build-time tooling for the generators in dev/, never bundled.
clojure -M:test # 17 tests: pure unit + live read-only sweeps
# (live sweeps self-skip when creds are unset)
clojure -M:dev -m verify-spec # static: Jira — every fn's method+path matches the spec
clojure -M:dev -m verify-spec-confluence # static: Confluence v2+v1 — same check, keyed by :api
clojure -M:dev -m find-recursion # static: no infinite same-arity self-recursion
The verify-spec* tasks are the conformance gate: they reconstruct each
function's {:method :path} and confirm a matching operation exists in the
OpenAPI spec. Current status: Jira 575/575 and Confluence 334/334 matched,
1:1, zero mismatches. They check method + path structure (not every
query-param/body-field name, which the functions pass through and document from
the schema).
Connect/Forge app-only endpoints (app properties, dynamic modules, app
migration, app-scoped custom-field options, etc.) are intentionally omitted —
they require app authentication, not user API tokens. See
dev/extract.clj for the exact partition and the skipped tag list.
Can you improve this documentation?Edit on GitHub
cljdoc builds & hosts documentation for Clojure/Script libraries
| Ctrl+k | Jump to recent docs |
| ← | Move to previous article |
| → | Move to next article |
| Ctrl+/ | Jump to the search field |