TeamAPI latest
On this page
  1. Five import sources
  2. Sync with GitHub teams
  3. Write back to Slack, Okta and PagerDuty

Import and synchronization

teamapi import github-org <org> --out <dir> bootstraps teamapi.yml files from an org that already exists on GitHub, instead of hand-writing them: one <team-id>/teamapi.yml per GitHub team, with members resolved from GitHub's user profiles (name, email, and the githubUsername that Sync with GitHub teams needs) and a services[] entry per repo the team owns.

$ teamapi import github-org acme-example --out ./imported
Wrote 4 team(s) to ./imported/ — every team defaulted to type: stream-aligned with no roles[]; review and adjust both by hand, then run `teamapi validate`.

GitHub teams carry no Team Topologies typing or role hierarchy, so every generated team defaults to type: stream-aligned with an empty roles[] — both are meant to be corrected by hand, not taken as ground truth. Run teamapi validate ./imported next, then fill in roles[], fix each team's type, and add cognitiveLoad/interactions/dependencies as you would for any hand-authored team. Requires a GitHub token via --token or GITHUB_TOKEN/GH_TOKEN.

Five import sources#

How much an org must enter by hand depends on the data it already has:

source argument needs gets you
github-org the org login GITHUB_TOKEN teams, members with githubUsername, services from repos
backstage a catalog file or URL groups, members, owned components/APIs, a guessed team type
okta your Okta org URL OKTA_TOKEN one team per directory group, with its people
slack SLACK_BOT_TOKEN a skeleton per channel: name, topic as focus, channel declared
csv a file teams, people, and roles[] from a job-title column
# Already have a Backstage catalog? Everything a Team API document wants is in it.
teamapi import backstage http://backstage.internal/api/catalog/entities --out ./teams

# Four hundred teams in Okta, none of them written down anywhere else.
teamapi import okta https://acme.okta.com --prefix eng- --out ./teams

# An HRIS export. The only source that can populate roles[].
teamapi import csv ./people.csv --out ./teams

# The list of teams exists nowhere but a channel sidebar. It happens.
teamapi import slack --match '^team-' --prefix team- --out ./teams

The importers make the following choices:

Every source is deliberately incomplete in the same way: nothing outside the source is invented, and what each one couldn't know is printed after the run.

Sync with GitHub teams#

Everything above reads the spec; teamapi apply is the one command that writes back to a real system — it reconciles actual GitHub teams and memberships in a GitHub org with what the spec declares, the way terraform plan/apply reconciles infrastructure. One GitHub team per Team API team, matched by slug === team id; members are resolved via each member's githubUsername (add it alongside contact — see the spec). A member with no githubUsername set is reported as skipped, not silently dropped from the plan.

It always prints a plan first. ACME Org's members don't carry a githubUsername (they're fictional), so running it as-is reports every member as skipped; add the field to see adds/removes, e.g.:

$ teamapi apply examples/acme-org --org acme-example
+ create team 'stream-checkout' in acme-example
  + add @diego-alves to 'stream-checkout'
  ! 'stream-checkout': 2 member(s) skipped, no githubUsername set: yuki-tanaka, fatima-al-sayed

Re-run with --yes to apply this plan.

Nothing is written until you re-run with --yes. A team that doesn't exist yet in GitHub is created (named after the team id, so its slug matches — rename it in GitHub afterward for a friendlier display name); an existing team's membership is diffed and only the difference (adds/removes) is applied. Requires a GitHub token with admin:org scope, via --token or GITHUB_TOKEN/GH_TOKEN.

Write back to Slack, Okta and PagerDuty#

apply has always written to GitHub teams. Slack, Okta, and PagerDuty began as read-only drift reports. Fixing every finding by hand in a UI did not keep up with the drift, so teamapi apply-to <slack|okta|pagerduty> now closes those loops with the same plan-then---yes flow as apply:

teamapi apply-to slack ./org
+ create @stream-checkout (stream-checkout)
  + add diego.alves@acme.example to @stream-checkout
  - remove U03KX9 from @platform-payments
  ! @enabling-devex: 1 member(s) with no matching Slack account: alex-tran

Re-run with --yes to apply this plan.
target what's written never written
slack usergroup membership, and the usergroup itself channels, channel membership
okta group membership groups themselves — created or deleted
pagerduty team membership schedules, escalation policies, teams

The Slack one is the one that pays for itself: @platform-payments in a message is how people actually reach a team, it's maintained by hand, and it's wrong within weeks of anybody joining or leaving — silently, in the one place where being wrong means the message reaches nobody.

The command leaves several operations out because the source data cannot perform them safely:

None of these APIs has a transaction, so a failure partway through says so and tells you to re-run, rather than reporting success over a half-applied change.