Employee data in JSM tickets ✅
Employee data in Assets ✅
Employee data in Teamwork Graph ❓
OnLink is known for the first two. Now, it handles the Teamwork Graph integration as well.
Here’s why it matters, and how to get there in a few steps.
Teamwork Graph is Atlassian’s unified data layer — the shared model underneath Jira, Confluence, and Rovo. Every piece of work data becomes an object of a known type: work items, documents, messages, users, projects, pull requests. Data from Jira, Slack, GitHub, Google Drive, and a hundred other connectors lands in the same model, with the same relationships and permissions, so it can be searched and reasoned over the same way regardless of where it came from.
The point of the graph isn’t storage. It’s context. Anything in the graph is available to Rovo Search, Rovo Chat, Rovo Agents, and Atlassian Analytics.
Once Workers, Positions, and Organizations are in Teamwork Graph, the AI layer stops guessing at your org.
Rovo Search gets people-aware. “Who leads platform engineering?” and “who reports to Dana?” become answerable from your actual HRIS, not from a stale Confluence page someone made in 2023.
Rovo Chat gets organizational context. Ask it to summarize what a team shipped last quarter and it can resolve “the team” to real people with real reporting lines — rather than a list of Jira assignees you have to interpret yourself.
Rovo Agents can route and act correctly. An approval agent needs to know a requester’s manager. An onboarding agent needs to know the department and start date. A triage agent needs to know which org owns a service. All of that is org data, and none of it lives in Jira.
Permissions and relationships come along for the ride. Graph objects carry their relationships, so the org structure is queryable — not just a flat list of names.
This is possible with OnLink.

OnLink’s Teamwork Graph bulk job pulls from any HR connection you’ve already configured — Workday, BambooHR, ADP, Oracle HCM, HiBob, or an Anysource custom REST API — maps it to Atlassian’s HRIS S3 format, and writes a full-snapshot CSV to your bucket on a schedule.
Each run fetches every record from the source, maps source fields to Teamwork Graph fields, normalizes values to Atlassian’s expected formats, and overwrites the fixed file name in S3. Atlassian’s connector picks it up on its next sync.
| Object type | File written |
|---|---|
| Workers | workers.csv |
| Positions | positions.csv |
| Organizations | organizations.csv |
Configuration Steps:
1. Prerequisites. In Atlassian: configure the Teamwork Graph HRIS S3 connector against your bucket, expecting those file names. In OnLink: a saved HR source connection, plus an S3 target connection (Connections → Add System → HRIS S3). You’ll use two identities — an IAM user with an access key for OnLink to PutObject, and an IAM role with an External ID for Atlassian to GetObject/ListBucket. (Atlassian requires the role name to start with atlassian-hris-s3.)
2. Create the job. Bulk Jobs → Add New Bulk Job. Set bulk job type to Teamwork Graph, pick the object type, pick your source and S3 target, and optionally turn on a schedule — hourly, daily, weekly, or monthly. Teamwork Graph jobs are a single screen; no JQL or HTTP body step.
3. Map the fields. The editor pre-fills one line per Teamwork Graph field. Replace the placeholder with the flattened field name from your HR API:
map:<source field>=<target field>
config:<key>=<value>Required fields by object:
workerId, createdAt, name, isActivepositionId, createdAt, isActive, organizationId, workerIdorganizationId, organizationName, createdAt, isActiveOptional fields can stay as placeholders and export as empty columns.
4. Let OnLink normalize. Dates become ISO UTC or YYYY-MM-DD, booleans become 1/0, enums get canonicalized to Atlassian’s accepted values. Two transformations handle the common gaps:
map:concat(firstName,' ',lastName)=name
map:if(status eq 'active','1','0')=isActive5. Test, then run. Click Fetch API Sample before saving — it confirms connectivity and shows you the exact source field names to use in your map: lines. Rows missing required fields get dropped; invalid optional values get blanked. Job logs report fetched N record(s) and uploaded workers.csv with N row(s).
Run order for a full org chart: Organizations → Positions → Workers, since positions reference organizations and workers.
Three jobs, one HiBob source connection, one S3 target.
HiBob Connection
Use the Service account created in HiBob for the connection. Please ensure they have read scope for employee fields.
The request body should have the fields for Teamwork Graph mapping. Here is the fields you can use:
{
“fields”: [
“root.id”,
“root.fullName”,
“root.creationDateTime”,
“root.email”,
“work.startDate”,
“work.title”,
“work.department”,
“address.country”,
“internal.status”,
“payroll.employment.contract”,
“payroll.employment.type”,
“employee.jobProfileTitle”,
“employee.jobLevelName”,
“employee.jobFamilyId”,
“employee.jobProfileCode”
],
“showInactive”: false,
“humanReadable”: “APPEND”
}
Job 1 — TG Organizations (source: HiBob department list)
map:id=organizationId
map:name=organizationName
map:parentId=superiorOrganizationId
map:createdAt=createdAt
map:if(archived eq 'false','1','0')=isActiveJob 2 — TG Positions (one position per employee)
map:concat('pos-', id)=positionId
map:employee.jobProfileTitle=positionTitle
map:work.title=jobTitle
map:creationDateTime=createdAt
map:if(internal.status eq ’Active’,’Filled’,’Unfilled’)=status
map:if(internal.status eq ’Active’,’1’,’0’)=isActive
map:employee.jobFamilyId=organizationId
map:id=workerId
map:worker.department=jobFamily
map:'Employee'= employmentType
map:address.country=country
map:id=externalPositionKey
map:employee.jobLevelName=levelJob 3 — TG Workers

map:id=workerId
map:creationDateTime=createdAt
map:fullName=name
map:if(internal.status eq ’Active’,’1’,’0’)=isActive
map:email=emailAddress
map:work.startDate=hireDate
map:work.title=title
map:address.country=country
map:worker.department=jobFamily
map:employee.jobProfileCode=levelSchedule all three daily. After the import, Teamwork Graph get the changes from HiBob.
If you’re already using Rovo, your graph is missing the one dataset that makes it organizationally aware. OnLink gets your employee data in.
Start a free trial of OnLink on the Atlassian Marketplace and set up your first Teamwork Graph bulk job in an afternoon.
Which HR systems are supported?
Any HR connection you can save in OnLink — Workday, BambooHR, ADP, Oracle HCM, SAP SuccessFactors, Paylocity, HiBob, Paychex, Dayforce, and others — plus Anysource, our generic REST connector for HRIS platforms without a packaged integration. The bulk job doesn’t care which source it reads from; only the field mappings change.
How fresh is the data?
OnLink runs on whatever schedule you set — hourly, daily, weekly, or monthly. Atlassian’s connector then ingests on its own cycle. Budget up to 24 hours for data to appear after you first configure the connector; after that, a daily OnLink job keeps the graph within a day of your HRIS.
Is each run incremental or a full refresh?
Full snapshot. Every run fetches all records and overwrites the file in S3. No drift, no reconciliation logic to maintain, no orphaned records from a missed delta.
Do I need all three object types?
Workers alone gets you people, emails, titles, and Talent features. Add Positions and Organizations when you want the actual org structure — reporting lines, departments, and hierarchy that Rovo can traverse. Run them in order: Organizations → Positions → Workers, since positions reference both.
How are terminated employees handled?
Map isActive to 0 and populate inactiveDate. Note Atlassian’s constraint: an inactive date has to fall within the past 30 days, so leavers should be deactivated on a regular schedule rather than in a once-a-year cleanup. Also, every reference between objects must point to an active entity — a position pointing at a deactivated worker will be rejected.
My HRIS field names don’t match Atlassian’s schema. Now what?
That’s the normal case. Click Fetch API Sample to see the exact flattened field names your source returns, then map them line by line. Two transformations cover most mismatches: concat(firstName,' ',lastName)=name when there’s no single full-name field, and if(status eq 'active','1','0')=isActive when your source uses a string status instead of a boolean.
Can I limit what employee data leaves my HRIS?
Yes. Only fields you explicitly map get written. Optional fields left as placeholders export as empty columns, so you can start with the required set plus email and expand later.
RELATED
