> ## Documentation Index
> Fetch the complete documentation index at: https://docs.roboticks.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Jama Connect

> Round-trip requirements between Jama and Roboticks via ReqIF. Field mapping, scheduled sync, link-role mapping, round-trip caveats.

# Jama Connect

Roboticks integrates with Jama Connect through **two paths**:

1. **Direct sync via the BYO connector framework** (v1) — Roboticks polls Jama's REST API on a schedule (cloud sync) or rbtk does it from inside your network (CLI push). Items, relationships, and tags land in the traceability matrix continuously.
2. **ReqIF round-trip** (free, always supported) — for snapshot-style audits or one-shot imports.

This page covers both. Jama's `primary_sync_mode` is **cloud sync** because Jama is SaaS-first and exposes a stable REST API; CLI push is available for self-hosted Jama deployments.

<Info>
  Jama's REST API is at `/rest/v1/`. Auth is OAuth2 client-credentials (preferred) or a Personal Access Token (PAT) for self-hosted Jama.
</Info>

## Connector type and pricing

|       |                                                        |
| ----- | ------------------------------------------------------ |
| Type  | BYO requirements connector                             |
| Tier  | Team (3 BYO connectors included), Enterprise (bundled) |
| Price | **\$149 / connector / month** above the Team allowance |

You bring the Jama license. For direct sync you provide either an OAuth2 client\_id + client\_secret (Jama Cloud admin → API Clients) or a PAT.

## v1 direct sync

```mermaid theme={null}
%%{init: {"theme": "neutral", "themeVariables": {"primaryColor": "#4040ff"}} }%%
flowchart LR
    Jama["Jama Connect"] -->|REST v1| Plat["Roboticks (cloud poll)"]
    Jama2["Jama Connect (on-prem)"] -->|REST v1| CLI["rbtk connector sync jama"]
    CLI -->|/internal/connectors/jama/ingest| Plat2["Roboticks"]
```

The adapter pulls:

| Jama entity                                   | Roboticks shape                                                                                                            |
| --------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------- |
| `items` (anything that isn't a Folder or Set) | `Requirement` with `external_id = documentKey`, `external_version = modifiedDate`, `external_url` deep-linking to the item |
| `relationships` per item                      | `RequirementRelation` (`verifies`, `derives`, `refines`, `relates_to`) with the raw Jama role preserved                    |
| Custom fields                                 | Stored verbatim in `attributes` JSONB — surfaced in the matrix UI without schema awareness                                 |

Incremental sync filters items by `modifiedDate.from={cursor}` — only changed items hit the wire.

### Cloud sync setup (OAuth2)

<Steps>
  <Step title="Create an OAuth2 client in Jama">
    **Jama → Admin → API Clients → Create OAuth Client**. Scope: read access to the project(s). Save the **client\_id** and **client\_secret**.
  </Step>

  <Step title="Subscribe in Roboticks">
    **Settings → Integrations → Requirements → Jama → Subscribe**. The setup wizard skips the CLI install step (Jama is cloud-primary) and goes directly to credentials:

    * **Base URL**: `https://acme.jamacloud.com`
    * **Client ID** + **Client Secret**
    * **Jama project ID** (numeric — find it under **Admin → Projects**)
  </Step>

  <Step title="Verify">
    The wizard calls `verify_credentials`; Jama returns 200 from `/rest/v1/projects`. If it does, the badge flips to "OK" and the first sync runs immediately.
  </Step>
</Steps>

### CLI push setup (on-prem Jama)

<Steps>
  <Step title="Install rbtk and authenticate">
    ```bash theme={null}
    pipx install roboticks-cli
    rbtk auth login --org-slug=acme
    ```
  </Step>

  <Step title="Configure with PAT or OAuth">
    ```bash theme={null}
    rbtk connector configure jama \
        --url=https://jama.acme.internal \
        --auth=oauth2 \
        --client-id=$JAMA_CLIENT_ID \
        --client-secret=$JAMA_CLIENT_SECRET \
        --label=primary \
        --jama-project-id=7
    ```

    Or for PAT auth (self-hosted Jama supports it):

    ```bash theme={null}
    rbtk connector configure jama \
        --url=https://jama.acme.internal \
        --auth=pat \
        --token=$JAMA_PAT \
        --label=primary --jama-project-id=7
    ```
  </Step>

  <Step title="Sync">
    ```bash theme={null}
    rbtk connector sync jama --label=primary
    ```
  </Step>
</Steps>

## ReqIF round-trip (alternative)

## One-time setup

<Steps>
  <Step title="Add the connector in Roboticks">
    **Settings → Integrations → Requirements → Add → Jama**. Name it for the project (e.g., `jama-warehouse`). Roboticks creates the connector record and bills the \$149/month line if over the Team allowance.
  </Step>

  <Step title="Upload a field mapping">
    Jama's workItem types and field names rarely match Roboticks' default vocabulary. Provide a YAML mapping:

    ```yaml theme={null}
    # roboticks/jama-mapping.yaml
    workitem_types:
      "System Requirement": functional
      "Safety Requirement":  safety
      "Performance Requirement": performance
    fields:
      "Identifier": id
      "Name":       title
      "Description": text
      "ASIL/PL":    asil_pl
    link_roles:
      "Derived from": derives_from
      "Verified by":  confirmed_by
      "Refines":      refines
    ```

    Upload via CLI:

    ```bash theme={null}
    rbtk integrations jama update --field-map roboticks/jama-mapping.yaml
    ```
  </Step>

  <Step title="Decide on the sync cadence">
    Three patterns work:

    * **Push-on-release** — export from Jama only at release tagging time
    * **Nightly** — scheduled CI job pulls a fresh export
    * **On-demand** — humans trigger when they make spec changes
  </Step>
</Steps>

## CI recipe

```yaml theme={null}
# .github/workflows/jama-sync.yml
name: Jama sync
on:
  schedule:
    - cron: "0 5 * * *"   # 05:00 UTC daily
  workflow_dispatch:

jobs:
  sync:
    runs-on: ubuntu-latest
    permissions:
      id-token: write
      contents: read
    steps:
      - uses: actions/checkout@v4

      - name: Pull Jama ReqIF export
        run: |
          curl -fsSL -u "${{ secrets.JAMA_USER }}:${{ secrets.JAMA_TOKEN }}" \
            "https://acme.jamacloud.com/rest/v1/projects/42/export?format=reqif" \
            -o jama-export.reqif

      - run: pipx install roboticks-cli
      - run: rbtk auth oidc-from-github

      - name: Upload to Roboticks
        run: |
          rbtk requirements upload jama-export.reqif \
            --mode update \
            --field-map roboticks/jama-mapping.yaml

      - name: Export Roboticks state back
        run: |
          rbtk requirements export \
            --format reqif --with-coverage \
            --out roboticks-export.reqif

      - name: Push back to Jama
        run: |
          curl -fsSL -u "${{ secrets.JAMA_USER }}:${{ secrets.JAMA_TOKEN }}" \
            -X POST -F "file=@roboticks-export.reqif" \
            "https://acme.jamacloud.com/rest/v1/projects/42/import?format=reqif"
```

The export-back loop carries Roboticks' `coverage_status` attribute (`confirmed | uncovered | stale`) into Jama's `ReqIF.ForeignAttributes`, where Jama exposes it as a sortable column.

## Field mapping reference

| Roboticks field    | Required | Typical Jama source                   |
| ------------------ | -------- | ------------------------------------- |
| `id`               | yes      | `Identifier` or `documentKey`         |
| `title`            | yes      | `Name`                                |
| `text`             | yes      | `Description`                         |
| `type`             | yes      | derived from `itemType`               |
| `asil_pl`          | optional | custom field, often `ASIL/PL`         |
| `priority`         | optional | `Priority`                            |
| `tags`             | optional | comma-split of `Tags`                 |
| derived-from links | optional | `Refines` / `Derived from` link roles |

## Round-trip caveats

<AccordionGroup>
  <Accordion title="Rich-text fields lose formatting">
    Jama stores `Description` as XHTML. Roboticks stores `text` as Markdown. The conversion preserves headings, lists, bold/italic, and inline code; it discards tables and embedded images. Use the Jama Web Viewer for the canonical rendering.
  </Accordion>

  <Accordion title="Roboticks-side annotations are advisory">
    `coverage_status` is exported as a foreign attribute. Jama displays it but does not gate workflows on it. If you want a Jama review state to block on coverage, wire a Jama webhook to your CI that fails when any pinned requirement is `uncovered`.
  </Accordion>

  <Accordion title="ID stability">
    Roboticks uses the value of the `id` field (default `Identifier`) as the immutable key. If Jama re-IDs a workItem (rare, usually via a migration), you'll get a duplicate; resolve by archiving the old ID via `rbtk requirements list --status archived`.
  </Accordion>

  <Accordion title="Link role mapping is one-way at first">
    The first round trip imports Jama links as Roboticks "derives\_from" or "refines" relationships. The export-back loop preserves them. New Roboticks-side relationships (e.g., a test annotating a requirement) appear in Jama under the `roboticks.confirmed_by` foreign attribute, not as native Jama links.
  </Accordion>
</AccordionGroup>

## Validation

After the first sync:

```bash theme={null}
rbtk requirements list --output json | jq '.[] | {id, title, type}'
```

Spot-check 5–10 requirements against Jama. Then run a `--dry-run`:

```bash theme={null}
rbtk requirements upload jama-export.reqif --dry-run --output json | jq '.changes'
```

The output shows `added`, `updated`, `archived`, `unchanged`. On a properly-converged sync, `added` is 0 unless someone authored a new requirement in Jama since the last run.

## Troubleshooting

<AccordionGroup>
  <Accordion title="`field-map: required field 'id' not produced`">
    Your mapping's `fields.id` source doesn't match Jama's actual field name. Re-export with `?fields=true` and inspect the column headers.
  </Accordion>

  <Accordion title="Duplicate requirement on every sync">
    Jama is re-keying. Pin the mapping to a stable field (e.g., `documentKey` instead of `Name`).
  </Accordion>

  <Accordion title="Round-trip export rejected by Jama">
    Jama enforces some constraints on import (e.g., `itemType` must exist). Check the upload log in Jama's **Project Admin → Imports**.
  </Accordion>
</AccordionGroup>

## Next

<CardGroup cols={2}>
  <Card title="Polarion" icon="diagram-project" href="/integrations/polarion">
    The other big RM tool — same shape, different quirks.
  </Card>

  <Card title="rbtk requirements" icon="terminal" href="/cli/requirements-commands">
    Upload, coverage, export reference.
  </Card>
</CardGroup>
