About atlassian-rest-ops
atlassian-rest-ops is an agent skill for performing Confluence Cloud v2 and Jira Cloud v3 operations through their REST APIs. It uses curl and bundled OpenAPI specifications to resolve endpoint details, with separate handling for each API's formats and behavior.
Key features
- 01Confluence Cloud v2 REST operations
- 02Jira Cloud v3 REST operations
- 03Endpoint lookup and schema resolution
- 04API-specific pagination and error handling
- 05Markdown-to-ADF conversion
Use cases
- 01Creating or updating Confluence pages
- 02Searching or transitioning Jira issues
- 03Posting formatted Jira or Confluence content
Install
Copy the command and run it in a terminal.
npx skills add https:/ / github.com/ bm629/ agent-skills --skill atlassian-rest-opsAfter installing, type/atlassian-rest-opsin Claude Code to confirm it loaded.
Bundled files
References and scripts installed along with SKILL.md.
assets/endpoint-index.mdreferences/credentials.mdforge-amendments.logpatterns.mdrich-text.mdsources.mdscripts/create-confluence-page.shcreate-confluence-page.sh.validation.mdcreate-jira-issue.shcreate-jira-issue.sh.validation.mdendpoint.pyendpoint.py.validation.mdlist-confluence-pages.shlist-confluence-pages.sh.validation.mdmd_to_adf.pymd_to_adf.py.validation.mdsearch-jira-issues.shsearch-jira-issues.sh.validation.mdDetails
From the project READMEatlassian-rest-ops
Overview
This skill lets an agent perform any Confluence Cloud (v2) or Jira Cloud (v3) operation by calling the REST API directly with curl — no SDK, no pip dependency. The REST API is the complete API surface, including writes such as creating a Confluence page. The agent consumes the credentials the caller injected, looks up an endpoint in a bundled OpenAPI spec via an endpoint index + a $ref-resolver, constructs the curl, and parses the JSON. Confluence and Jira differ on several axes (base URL, pagination, errors, rich-text), so this skill carries per-API patterns rather than one generic shape.
When to activate
- ✅ Performing a Confluence Cloud v2 operation (create/get/update/search pages, spaces, attachments, …).
- ✅ Performing a Jira Cloud v3 operation (create/search/transition issues, comments, …).
- ✅ A write or operation you need done programmatically against the Atlassian REST API (e.g. creating a Confluence page).
Do NOT activate when:
- The target is Atlassian Server / Data Center (this skill is Cloud + API-token only).
- You only need credential setup — credentials are provided by the caller; this skill does not provision or resolve them (see
references/credentials.mdfor the contract it consumes).
Workflow
Step 1 — Receive the injected credentials
The caller has already resolved the account and injected what this operation needs — consume it; do not look for a record yourself. You receive from context:
base_urlandemail(and the capability the account acts under) — as context values, not read from a file.- The token, by an ordered load rule the context carries the variable NAME for. Resolve that name as: the project-level
.envvalue if that file exists and defines the var, else the environment variable of that name — project.envis tried first (that is how a project.envoverrides a global env var). The token value is never in the context prose — only its variable name;curlreads the value from the environment. The project root is supplied by the context; perform no scope resolution or directory walk to find the.env, and it is project.env, not.envrc.
Then bridge these into the example scripts' fixed vars ATLASSIAN_EMAIL / ATLASSIAN_BASE_URL / ATLASSIAN_API_TOKEN (same bridge as before; only the inputs now come from context). Full contract + the bridge: references/credentials.md.
Step 2 — Find the endpoint
Scan assets/endpoint-index.md (one line per operation: METHOD path — summary (operationId)) for the operation you need. Never load a full spec into context — they are multi-MB.
Step 3 — Resolve the call shape
Run the resolver: python3 scripts/endpoint.py <confluence|jira> <operationId>. It dereferences that one operation from the bundled OpenAPI spec ($ref-resolved) and prints the method, path, params, request/response schema, and a curl skeleton. (scripts/endpoint.py + its validation are added during augmentation; see Progressive disclosure.)
Step 4 — Construct + run the curl
Apply the per-API patterns from references/patterns.md:
- Auth (both):
curl -u "$email:$<token_env>"(HTTP Basic). - Base URL: Confluence →
<base_url>/wiki/api/v2/<path>; Jira →<base_url>/<path>(paths already include/rest/api/3/…). - Rich-text bodies (the easy-to-get-wrong part) — see
references/rich-text.md: Jira uses ADF as a raw JSON object; Confluence uses{representation, value}whereatlas_doc_formatmeans the ADF JSON stringified andstoragemeans an XHTML string. If your source is Markdown (e.g. a formatted comment), convert it to ADF withpython3 scripts/md_to_adf.py < body.mdrather than sending the raw Markdown (which renders##/**/|literally). - Send/accept
application/json.
Step 5 — Handle the response
- Pagination: Confluence → cursor + follow
_links.next; Jira →startAt/maxResults(+total/isLast), ornextPageTokenon newer endpoints. Seereferences/patterns.md. - Errors: Jira returns
ErrorCollection{errorMessages, errors, status}; Confluence returns inline error JSON. On HTTP 429 (Jira) back off and retry.
Rules
Hard rules (never violate):
- No SDK, no
pip. API calls usecurl; the only helper isscripts/endpoint.py(python3stdlib only). - Never read or print the token value. Reference it only as
$<token_env>in acurl -uargument; the subprocess reads it from the environment. The token lives only in.env(gitignored). - Resolve before constructing. Build a request body/params from the
$ref-resolved schema (Step 3) or the verified rich-text formats — never from a guessed/remembered field set. - Per-API, not generic. Apply Confluence vs Jira patterns correctly (base URL, pagination, rich-text, errors) — they differ.
- Rich-text: Jira ADF = raw object; Confluence
atlas_doc_format= stringified ADF invalue. Do not mix them. - This skill never writes credentials. Credentials are provided by the caller; this skill never provisions or resolves them.
Preferences (override-able):
- Prefer the newer Jira issue search
GET /rest/api/3/search/jql(nextPageToken) over the legacy/search(startAt). - Request only needed fields (
fields=on Jira,body-format=on Confluence) to keep responses small.
Gotchas
- Rich-text mismatch. Sending a raw ADF object to Confluence
atlas_doc_format(instead of a stringified one), or a stringified blob to Jira (instead of a raw object), is the most common failure. Confluencevalueis always a string. - Base-URL difference. Confluence paths are relative to
…/wiki/api/v2; Jira paths already include/rest/api/3. Don't double-prefix or drop/wiki. $refchains. The request body is rarely inline in the spec — it$refs a schema (which may$reffurther). Use the resolver (Step 3); a rawgrepof the spec won't give you the full shape.- Legacy vs new Jira search.
GET /rest/api/3/searchis being deprecated forGET /rest/api/3/search/jql; pagination differs (startAtvsnextPageToken). - Rate limits. Jira returns HTTP 429 with a
Retry-After; honor it. Confluence may return 413 on oversized bodies. - Confluence delete is two-stage (verified live, applies to pages, blogposts, and other content).
DELETE /pages/{id}(or/blogposts/{id}) only trashes it (returns 204, butGETstill returns it withstatus: trashed). To remove permanently, callDELETE /<type>/{id}?purge=true— which only works once trashed (purge-without-trash is a no-op). A plain delete is not full removal. - Jira
/search/jqlrejects unbounded JQL (verified live). Ordering-only or empty JQL →400 ErrorCollection("Unbounded JQL queries are not allowed here"). Always include a search restriction (e.g.project = X,created >= -30d).
Anti-patterns
- Guessing the body. Don't hand-write a request body from memory of "what Jira issues look like" — resolve the schema; field requirements change per project/screen.
- Echoing the token. Never
echo $TOKEN, never paste it into a command you print, never write its value into any file. Reference the env var insidecurlonly. - Loading the whole spec. Don't
cat/read the multi-MB OpenAPI JSON into context — scan the index, resolve one op. - One-size-fits-all. Don't apply Jira's pagination/errors/ADF to Confluence or vice-versa.
- SDK creep. Don't reach for
atlassian-python-api/pip install—curl+ the resolver cover every endpoint.
Output
This skill produces API side effects (the requested Confluence/Jira operation) and returns the parsed JSON response to the calling agent. It writes no files of its own (it only consumes the caller-injected credentials). For write operations it reports the created/updated resource (id, key, URL); for reads it returns the result set, following pagination as needed. The abstract consumer is the calling agent (or a sub-agent) that needs the operation performed; secrets never enter that output.
Related
references/credentials.md— the credential contract this skill consumes (caller-injected fields + the ordered token-load rule + the bridge into the fixed vars).- The REST-direct + bundled-OpenAPI-spec pattern generalizes to other API providers.
Progressive disclosure
Heavy content lives in subfolders, loaded only on demand:
references/patterns.md— per-API patterns: auth, base URL, pagination (Confluence cursor vs Jira offset/token), response envelopes, errors, rate limits,expand. Load in Steps 4–5.references/rich-text.md— ADF (Jira, raw object) and Confluence body representations (storage/atlas_doc_format, value-as-string) with worked examples + the cross-API gotcha. Load when building a request body.references/credentials.md— the credential contract this skill consumes: caller-injected fields, the ordered token-load rule, the bridge into the fixed vars, honest-secret handling. Load in Step 1.references/sources.md— provenance (Atlassian official docs + the bundled spec versions).
Added during augmentation (Phase 2.C), referenced above:
assets/confluence-v2.json,assets/jira-v3.json— the bundled OpenAPI specs (authoritative; queried, never loaded wholesale).assets/endpoint-index.md— one line per operation, for discovery (Step 2).scripts/endpoint.py+scripts/endpoint.py.validation.md— thepython3$ref-resolver (Step 3).scripts/<op>.sh+.validation.md— the four validated examplecurls (create Confluence page, Confluence list, Jira create issue, Jira search).scripts/md_to_adf.py+.validation.md— a stdlib Markdown -> ADF converter. Pipe a Markdown comment/description through it to post it as native ADF (headings, bold, code, links, lists, GFM tables render instead of showing raw##/|):python3 scripts/md_to_adf.py < comment.md-> the ADF object for the Jira{"body": ...}.
Standalone usage (optional, not required)
This is a convenience for a human running the skill by hand outside agent-flow — it is not a dependency of the skill. The skill's normative contract is caller-injection (Step 1); this appendix is only the manual-operator bridge.
To run by hand, populate the three fixed vars yourself from a .service-accounts.yaml record + its .env token, then run the scripts. An example record:
accounts:
- name: atlassian-work
provider: atlassian
base_url: https://workco.atlassian.net
email: [email protected]
token_env: ATLASSIAN_WORK_API_TOKEN # the var holding the token value; value lives in .env (gitignored)
set -a; source .env; set +a # loads $ATLASSIAN_WORK_API_TOKEN, never prints it
export ATLASSIAN_EMAIL="[email protected]"
export ATLASSIAN_BASE_URL="https://workco.atlassian.net"
export ATLASSIAN_API_TOKEN="$ATLASSIAN_WORK_API_TOKEN"
bash scripts/create-confluence-page.sh <space-id> "<title>"
Get an API token at id.atlassian.com → Security → "Create API token". The value is referenced by name only, never printed.
Body budget
description≤ 1,024 chars (agentskills.io cap).- Body ≤ ~500 lines / 5,000 tokens — kept in context every turn; per-API detail lives in
references/. assets/*.jsonare large (queried on disk, never loaded into context).