Platform

Platform overviewArchitectureWorkflow orchestrationGitOps configurationGovernance and AAAAI and MCPKnowledge and contextRuntime and executionEvidence and monitoring

Use cases

All use casesProduction incidentRelease preparationHotfix to productionSecurity scan triage
Why NopsAIIntegrationsSecurity

Resources

All resourcesAI agent governanceMCP governanceMCP securitySelf-hosted platforms
PricingGitHub

Company

How a run worksAboutContactBook a demo

Teams API

The ownership tree, the applications under it, and the team-owned models, agent roles, and MCP profiles.

ReferenceAdministratorDeveloper

Key points

  • The team list is a tree with children populated, so one call gives the whole hierarchy.
  • Moving a team changes its path, and the path is what scopes, grants, and notifications refer to.
  • Moving an application is expressed by the team in the URL, not a field in the body.
  • An application is a team whose kind is application: it owns a repository rather than other teams.
  • Team defaults decide what a pipeline resolves to when it names no model or agent role; they never override one that does.
  • default is a literal path segment on the models and agent-roles routes, so a profile cannot be named default.
  • The collection PUT on models replaces the set — anything omitted is removed. The single-profile PUT does not.
  • Teams choose which events they hear about; the platform decides how they are delivered.

Operations

GET/v1/teamsAuthorized

Lists the team tree.

Notes

Returns a tree rather than a flat list, so a client walks children instead of joining on parent_id.

Call it

Read the team treeapi-teams request
curl -s -H "Authorization: Bearer $NOPSAI_TOKEN" "$NOPSAI_URL/v1/teams" | jq
Result

A nested tree. children is populated, so one call gives the whole hierarchy.

Responses

200application/json

The team tree the caller may see.

[
  {
    "id": 12,
    "name": "platform",
    "kind": "team",
    "parent_id": null,
    "children": [
      { "id": 18, "name": "payments", "kind": "team", "parent_id": 12, "children": [] }
    ]
  }
]

Side effects

  • None.

Proven by

  • services/nopsai/team_handlers_test.go
  • services/nopsai/team_handlers.go
  • pkg/models/types.go
POST/v1/teamsAuthorized

Creates a team.

Notes

The team path is built from the hierarchy, so a team’s position is part of its identity for scopes, grants, and notifications.

Call it

Create a team under anotherapi-teams request
curl -sX POST "$NOPSAI_URL/v1/teams" \
  -H "Authorization: Bearer $NOPSAI_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"name":"payments","parent_id":12}' | jq
Result

The created team. Omit parent_id to create at the root.

Responses

201application/json

Team created.

{
  "id": 18,
  "name": "payments",
  "kind": "team",
  "parent_id": 12,
  "children": []
}

When it fails

StatusCauseWhat to do
400A missing name, an unusable name, or a parent that does not exist.The message names the field.

Side effects

  • Creates an ownership boundary other resources can be assigned to.
  • Writes an audit record.

Proven by

  • services/nopsai/team_handlers_test.go
  • services/nopsai/team_handlers.go
GET/v1/teams/{teamID}Authorized

Reads one team.

Parameters

NameInTypeRequiredDescription
teamIDpathintegerRequiredTeam identifier.

Call it

Read a teamapi-teams request
curl -s -H "Authorization: Bearer $NOPSAI_TOKEN" "$NOPSAI_URL/v1/teams/$TEAM_ID" | jq
Result

The team with its children and repository metadata.

Responses

200application/json

The team.

{
  "id": 18,
  "name": "payments",
  "kind": "team",
  "parent_id": 12,
  "children": []
}

When it fails

StatusCauseWhat to do
500The team could not be loaded.Platform fault.

Side effects

  • None.

Proven by

  • services/nopsai/team_handlers_test.go
  • services/nopsai/team_handlers.go
PUT/v1/teams/{teamID}Authorized

Renames or moves a team.

Notes

Moving a team is not a cosmetic rename: the path is the identity other resources refer to.

Parameters

NameInTypeRequiredDescription
teamIDpathintegerRequiredTeam to update.

Call it

Move a team to the rootapi-teams request
curl -sX PUT "$NOPSAI_URL/v1/teams/$TEAM_ID" \
  -H "Authorization: Bearer $NOPSAI_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"name":"payments","parent_id":null}' | jq
Result

The updated team. An explicit null parent moves it to Global; omitting the field keeps the current parent.

Replace before running
  • Omitted parent fields keep the current parent — only an explicit null moves a team.

Responses

200application/json

Team updated.

{
  "id": 18,
  "name": "payments",
  "kind": "team",
  "parent_id": null,
  "children": []
}

When it fails

StatusCauseWhat to do
400A name collision, or a move that would make the tree cyclic.A team cannot become its own descendant.

Side effects

  • Moving a team changes its path, and with it every scope, grant, and notification that referenced the old path.
  • Writes an audit record.

Proven by

  • services/nopsai/team_handlers_test.go
  • services/nopsai/team_handlers.go
DELETE/v1/teams/{teamID}Authorized

Deletes a team.

Notes

Check what the team owns first — pipelines, scopes, dashboards, and grants all hang off its path.

Parameters

NameInTypeRequiredDescription
teamIDpathintegerRequiredTeam to delete.

Call it

Delete a teamapi-teams request
curl -sX DELETE -H "Authorization: Bearer $NOPSAI_TOKEN" "$NOPSAI_URL/v1/teams/$TEAM_ID" -w "%{http_code}\n"
Result

204.

Responses

204

Team deleted.

When it fails

StatusCauseWhat to do
500The delete failed.Retry.

Side effects

  • Resources that referenced the team path stop resolving their owner.
  • Writes an audit record.

Proven by

  • services/nopsai/team_handlers_test.go
  • services/nopsai/team_handlers.go
GET/v1/teams/{teamID}/applicationsAuthorized

Lists applications under a team.

Notes

An application is a team whose kind is application: it owns a repository rather than other teams.

Parameters

NameInTypeRequiredDescription
teamIDpathintegerRequiredTeam identifier.

Call it

List applicationsapi-teams request
curl -s -H "Authorization: Bearer $NOPSAI_TOKEN" "$NOPSAI_URL/v1/teams/$TEAM_ID/applications" | jq
Result

Repository-backed leaves under the team.

Responses

200application/json

Applications under the team.

[{
  "id": 31,
  "name": "payments-api",
  "kind": "application",
  "parent_id": 18,
  "repository_full_name": "acme/payments",
  "repo_url": "https://git.example.com/acme/payments"
}]

Side effects

  • None.

Proven by

  • services/nopsai/team_handlers_test.go
  • services/nopsai/team_handlers.go
POST/v1/teams/{teamID}/applicationsAuthorized

Creates an application under a team.

Parameters

NameInTypeRequiredDescription
teamIDpathintegerRequiredParent team.

Call it

Create an applicationapi-teams request
curl -sX POST "$NOPSAI_URL/v1/teams/$TEAM_ID/applications" \
  -H "Authorization: Bearer $NOPSAI_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"name":"payments-api","repository_full_name":"acme/payments"}' | jq
Result

The application, now the owner of runs triggered from that repository.

Responses

201application/json

Application created.

{
  "id": 31,
  "name": "payments-api",
  "kind": "application",
  "parent_id": 18,
  "repository_full_name": "acme/payments",
  "repo_url": "https://git.example.com/acme/payments"
}

When it fails

StatusCauseWhat to do
400A missing name or an unusable repository reference.The message names the field.
500The application could not be created.Retry.

Side effects

  • Runs triggered from the repository resolve their owner to this application.
  • Writes an audit record.

Proven by

  • services/nopsai/team_handlers_test.go
  • services/nopsai/team_handlers.go
PUT/v1/teams/{teamID}/applications/{applicationID}Authorized

Updates or moves an application.

Notes

The target parent is the team in the URL. Sending a parent in the body does not move anything.

Parameters

NameInTypeRequiredDescription
teamIDpathintegerRequiredTarget parent team — moving an application means calling this under the new parent.
applicationIDpathintegerRequiredApplication identifier.

Call it

Move an application to another teamapi-teams request
curl -sX PUT "$NOPSAI_URL/v1/teams/$NEW_TEAM_ID/applications/$APPLICATION_ID" \
  -H "Authorization: Bearer $NOPSAI_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"name":"payments-api"}' | jq
Result

The application under its new parent. The move is expressed by the team in the URL, not a field in the body.

Responses

200application/json

Application updated.

{
  "id": 31,
  "name": "payments-api",
  "kind": "application",
  "parent_id": 18,
  "repository_full_name": "acme/payments",
  "repo_url": "https://git.example.com/acme/payments"
}

When it fails

StatusCauseWhat to do
400An unusable name or repository reference.The message names the field.
500The update failed.Retry.

Side effects

  • Changes which team owns runs from the repository.
  • Writes an audit record.

Proven by

  • services/nopsai/team_handlers_test.go
  • services/nopsai/team_handlers.go
DELETE/v1/teams/{teamID}/applications/{applicationID}Authorized

Deletes an application.

Parameters

NameInTypeRequiredDescription
teamIDpathintegerRequiredParent team.
applicationIDpathintegerRequiredApplication to delete.

Call it

Delete an applicationapi-teams request
curl -sX DELETE -H "Authorization: Bearer $NOPSAI_TOKEN" "$NOPSAI_URL/v1/teams/$TEAM_ID/applications/$APPLICATION_ID" -w "%{http_code}\n"
Result

204. Runs already recorded against it stay.

Responses

204

Application deleted.

When it fails

StatusCauseWhat to do
500The delete failed.Retry.

Side effects

  • Repository events lose their owner resolution until another application claims the repository.
  • Writes an audit record.

Proven by

  • services/nopsai/team_handlers_test.go
  • services/nopsai/team_handlers.go
GET/v1/teams/{teamID}/defaultsAuthorized

Reads a team’s default AI resources.

Notes

Defaults are what a pipeline resolves to when it names no model or agent role. They do not override a pipeline that names one.

Parameters

NameInTypeRequiredDescription
teamIDpathintegerRequiredTeam identifier.

Call it

Read team defaultsapi-teams request
curl -s -H "Authorization: Bearer $NOPSAI_TOKEN" "$NOPSAI_URL/v1/teams/$TEAM_ID/defaults" | jq
Result

The model and agent role a pipeline in this team gets when it names none.

Responses

200application/json

Team defaults.

{
  "default_model": "reasoning-large",
  "default_agent_role": "senior-release-engineer"
}

When it fails

StatusCauseWhat to do
405A method other than GET or PUT.Use GET to read, PUT to set.
500Defaults could not be loaded.Platform fault.

Side effects

  • None.

Proven by

  • services/nopsai/team_profile_resolution_test.go
  • services/nopsai/team_handlers.go
PUT/v1/teams/{teamID}/defaultsAuthorized

Sets a team’s default AI resources.

Parameters

NameInTypeRequiredDescription
teamIDpathintegerRequiredTeam identifier.

Call it

Set team defaultsapi-teams request
curl -sX PUT "$NOPSAI_URL/v1/teams/$TEAM_ID/defaults" \
  -H "Authorization: Bearer $NOPSAI_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"default_model":"reasoning-large"}' | jq
Result

The stored defaults, applied to the team’s next runs.

Responses

200application/json

Defaults stored.

{
  "default_model": "reasoning-large"
}

When it fails

StatusCauseWhat to do
400A profile that does not exist or is not allowed for the team.List the team’s models first.
500The defaults could not be stored.Retry.

Side effects

  • Changes what pipelines in this team resolve to when they name nothing.
  • Writes an audit record.

Proven by

  • services/nopsai/team_profile_resolution_test.go
  • services/nopsai/team_handlers.go
GET/v1/teams/{teamID}/notificationsAuthorized

Reads a team’s notification settings.

Notes

Teams choose what they hear about; the platform decides how it is delivered. The two are configured in different places on purpose.

Parameters

NameInTypeRequiredDescription
teamIDpathintegerRequiredTeam identifier.

Call it

Read team notificationsapi-teams request
curl -s -H "Authorization: Bearer $NOPSAI_TOKEN" "$NOPSAI_URL/v1/teams/$TEAM_ID/notifications" | jq
Result

What the team is told about, and where. Delivery itself is configured platform-wide.

Responses

200application/json

Notification settings for the team.

{
  "recipients": ["[email protected]"],
  "events": ["run.failure", "approval.requested"]
}

When it fails

StatusCauseWhat to do
405A method other than GET, PUT, or DELETE.Those three are supported.

Side effects

  • None.

Proven by

  • services/nopsai/team_handlers_test.go
  • services/nopsai/team_handlers.go
PUT/v1/teams/{teamID}/notificationsAuthorized

Sets a team’s notification settings.

Notes

Test platform mail settings before trusting a subscription: a valid subscription with broken delivery looks identical to no subscription.

Parameters

NameInTypeRequiredDescription
teamIDpathintegerRequiredTeam identifier.

Call it

Subscribe a team to run failuresapi-teams request
curl -sX PUT "$NOPSAI_URL/v1/teams/$TEAM_ID/notifications" \
  -H "Authorization: Bearer $NOPSAI_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"recipients":["[email protected]"],"events":["run.failure"]}' | jq
Result

The stored settings. Nothing is delivered until platform mail settings work.

Responses

200application/json

Settings stored.

{
  "recipients": ["[email protected]"],
  "events": ["run.failure"]
}

When it fails

StatusCauseWhat to do
400An unusable recipient or unknown event.The message names the value.
405An unsupported method.Use GET, PUT, or DELETE.

Side effects

  • Changes who is emailed about this team’s runs.
  • Writes an audit record.

Proven by

  • services/nopsai/team_handlers_test.go
  • services/nopsai/team_handlers.go
DELETE/v1/teams/{teamID}/notificationsAuthorized

Clears a team’s notification settings.

Parameters

NameInTypeRequiredDescription
teamIDpathintegerRequiredTeam identifier.

Call it

Unsubscribe a teamapi-teams request
curl -sX DELETE -H "Authorization: Bearer $NOPSAI_TOKEN" "$NOPSAI_URL/v1/teams/$TEAM_ID/notifications" -w "%{http_code}\n"
Result

The team stops being notified.

Responses

204

Settings cleared.

When it fails

StatusCauseWhat to do
405An unsupported method.Use GET, PUT, or DELETE.

Side effects

  • The team stops receiving notifications.
  • Writes an audit record.

Proven by

  • services/nopsai/team_handlers_test.go
  • services/nopsai/team_handlers.go
GET/v1/teams/{teamID}/modelsAuthorized

Lists a team’s model profiles.

Notes

The list includes cached team-owned profiles and what the team inherits, which is why a profile can appear here without being defined here.

Parameters

NameInTypeRequiredDescription
teamIDpathintegerRequiredTeam identifier.

Call it

List team modelsapi-teams request
curl -s -H "Authorization: Bearer $NOPSAI_TOKEN" "$NOPSAI_URL/v1/teams/$TEAM_ID/models" | jq
Result

The team’s own profiles plus the inherited ones it may use.

Responses

200application/json

Model profiles available to the team.

[
  {
    "name": "reasoning-large",
    "provider": "anthropic",
    "enabled": true,
    "is_default": true
  }
]

When it fails

StatusCauseWhat to do
500The query failed.Platform fault.

Side effects

  • None.

Proven by

  • services/nopsai/team_profile_handlers_test.go
  • services/nopsai/team_profile_handlers.go
PUT/v1/teams/{teamID}/modelsAuthorized

Replaces a team’s model profile set.

Parameters

NameInTypeRequiredDescription
teamIDpathintegerRequiredTeam identifier.

Call it

Replace the team’s modelsapi-teams request
curl -sX PUT "$NOPSAI_URL/v1/teams/$TEAM_ID/models" \
  -H "Authorization: Bearer $NOPSAI_TOKEN" \
  -H "Content-Type: application/json" \
  --data @models.json | jq
Result

The stored set. Omitted profiles are removed.

Replace before running
  • models.json is the full profile list for this team.

Responses

200application/json

Profiles stored.

[
  { "name": "reasoning-large", "provider": "anthropic", "enabled": true }
]

When it fails

StatusCauseWhat to do
400An invalid profile or a credential reference that does not resolve.The message names the profile.
500The profiles could not be stored.Retry.

Side effects

  • Pipelines naming a removed profile fail authorization on their next run.
  • Writes an audit record.

Proven by

  • services/nopsai/team_profile_handlers_test.go
  • services/nopsai/team_profile_handlers.go
PUT/v1/teams/{teamID}/models/defaultAuthorized

Sets the team’s default model profile.

Notes

The path segment is the literal word default, which is why a profile may not be named default.

Parameters

NameInTypeRequiredDescription
teamIDpathintegerRequiredTeam identifier.

Call it

Set the default modelapi-teams request
curl -sX PUT "$NOPSAI_URL/v1/teams/$TEAM_ID/models/default" \
  -H "Authorization: Bearer $NOPSAI_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"name":"reasoning-large"}' | jq
Result

The default a pipeline resolves to when it names no model.

Responses

200application/json

Default set.

{ "name": "reasoning-large", "is_default": true }

When it fails

StatusCauseWhat to do
400The profile does not exist or is disabled.List the team’s models first.
500The default could not be stored.Retry.

Side effects

  • Changes what unnamed model references resolve to.
  • Writes an audit record.

Proven by

  • services/nopsai/team_profile_resolution_test.go
  • services/nopsai/team_profile_handlers.go
PUT/v1/teams/{teamID}/models/{profileName}Authorized

Creates or replaces one team model profile.

Notes

Use this rather than the collection PUT when changing one profile: the collection form removes anything it omits.

Parameters

NameInTypeRequiredDescription
teamIDpathintegerRequiredTeam identifier.
profileNamepathstringRequiredProfile name.

Call it

Upsert one model profileapi-teams request
curl -sX PUT "$NOPSAI_URL/v1/teams/$TEAM_ID/models/reasoning-large" \
  -H "Authorization: Bearer $NOPSAI_TOKEN" \
  -H "Content-Type: application/json" \
  --data @profile.json | jq
Result

The stored profile, leaving the team’s other profiles untouched.

Replace before running
  • profile.json carries the provider, model, and credential reference.

Responses

200application/json

Profile stored.

{ "name": "reasoning-large", "provider": "anthropic", "enabled": true }

When it fails

StatusCauseWhat to do
400An invalid profile or unresolvable credential.The message names the field.
500The profile could not be stored.Retry.

Side effects

  • Writes an audit record.

Proven by

  • services/nopsai/team_profile_handlers_test.go
  • services/nopsai/team_profile_handlers.go
DELETE/v1/teams/{teamID}/models/{profileName}Authorized

Deletes a team model profile.

Parameters

NameInTypeRequiredDescription
teamIDpathintegerRequiredTeam identifier.
profileNamepathstringRequiredProfile to delete.

Call it

Delete a model profileapi-teams request
curl -sX DELETE -H "Authorization: Bearer $NOPSAI_TOKEN" "$NOPSAI_URL/v1/teams/$TEAM_ID/models/reasoning-large" -w "%{http_code}\n"
Result

Pipelines naming it fail authorization on their next run.

Responses

204

Profile deleted.

When it fails

StatusCauseWhat to do
500The delete failed.Retry.

Side effects

  • Pipelines referencing the profile stop resolving it.
  • Writes an audit record.

Proven by

  • services/nopsai/team_profile_handlers_test.go
  • services/nopsai/team_profile_handlers.go
GET/v1/teams/{teamID}/agent-rolesAuthorized

Lists a team’s agent roles.

Parameters

NameInTypeRequiredDescription
teamIDpathintegerRequiredTeam identifier.

Call it

List team agent rolesapi-teams request
curl -s -H "Authorization: Bearer $NOPSAI_TOKEN" "$NOPSAI_URL/v1/teams/$TEAM_ID/agent-roles" | jq
Result

The personas a pipeline in this team may name.

Responses

200application/json

Agent roles available to the team.

[
  { "id": "senior-release-engineer", "name": "Senior release engineer", "enabled": true }
]

When it fails

StatusCauseWhat to do
500The query failed.Platform fault.

Side effects

  • None.

Proven by

  • services/nopsai/team_profile_handlers_test.go
  • services/nopsai/team_profile_handlers.go
POST/v1/teams/{teamID}/agent-rolesAuthorized

Creates a team agent role.

Notes

A task cannot set agent_role; it comes from the pipeline or the step. Creating one here makes it nameable at those two levels.

Parameters

NameInTypeRequiredDescription
teamIDpathintegerRequiredTeam identifier.

Call it

Create an agent roleapi-teams request
curl -sX POST "$NOPSAI_URL/v1/teams/$TEAM_ID/agent-roles" \
  -H "Authorization: Bearer $NOPSAI_TOKEN" \
  -H "Content-Type: application/json" \
  --data @role.json | jq -r .id
Result

The created role, ready to name from agent_role in a pipeline or step.

Replace before running
  • role.json carries the display name and instructions.

Responses

201application/json

Agent role created.

{ "id": "senior-release-engineer", "name": "Senior release engineer", "enabled": true }

When it fails

StatusCauseWhat to do
400A missing name or instructions.The message names the field.
500The role could not be created.Retry.

Side effects

  • Writes an audit record.

Proven by

  • services/nopsai/team_profile_handlers_test.go
  • services/nopsai/team_profile_handlers.go
GET/v1/teams/{teamID}/agent-roles/{profileID}Authorized

Reads one team agent role.

Parameters

NameInTypeRequiredDescription
teamIDpathintegerRequiredTeam identifier.
profileIDpathstringRequiredAgent role identifier.

Call it

Read an agent roleapi-teams request
curl -s -H "Authorization: Bearer $NOPSAI_TOKEN" "$NOPSAI_URL/v1/teams/$TEAM_ID/agent-roles/senior-release-engineer" | jq
Result

The role with its instructions, which become part of the prompt for steps that name it.

Responses

200application/json

The agent role.

{ "id": "senior-release-engineer", "name": "Senior release engineer", "enabled": true }

When it fails

StatusCauseWhat to do
500The role could not be loaded.Platform fault.

Side effects

  • None.

Proven by

  • services/nopsai/team_profile_handlers_test.go
  • services/nopsai/team_profile_handlers.go
PUT/v1/teams/{teamID}/agent-roles/{profileID}Authorized

Replaces a team agent role.

Parameters

NameInTypeRequiredDescription
teamIDpathintegerRequiredTeam identifier.
profileIDpathstringRequiredAgent role identifier.

Call it

Update an agent roleapi-teams request
curl -sX PUT "$NOPSAI_URL/v1/teams/$TEAM_ID/agent-roles/senior-release-engineer" \
  -H "Authorization: Bearer $NOPSAI_TOKEN" \
  -H "Content-Type: application/json" \
  --data @role.json | jq
Result

The stored role. Runs already executing keep the instructions they started with.

Responses

200application/json

Role stored.

{ "id": "senior-release-engineer", "name": "Senior release engineer", "enabled": true }

When it fails

StatusCauseWhat to do
400Invalid instructions or a disabled state that conflicts with the team default.The message names the field.
500The update failed.Retry.

Side effects

  • Changes the prompt for every future step naming this role.
  • Writes an audit record.

Proven by

  • services/nopsai/team_profile_handlers_test.go
  • services/nopsai/team_profile_handlers.go
PUT/v1/teams/{teamID}/agent-roles/defaultAuthorized

Sets the team’s default agent role.

Notes

As with models, default is a literal path segment rather than a role id.

Parameters

NameInTypeRequiredDescription
teamIDpathintegerRequiredTeam identifier.

Call it

Set the default agent roleapi-teams request
curl -sX PUT "$NOPSAI_URL/v1/teams/$TEAM_ID/agent-roles/default" \
  -H "Authorization: Bearer $NOPSAI_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"id":"senior-release-engineer"}' | jq
Result

The role a pipeline resolves to when it names none.

Responses

200application/json

Default set.

{ "id": "senior-release-engineer", "is_default": true }

When it fails

StatusCauseWhat to do
400The role does not exist or is disabled.List the team’s agent roles first.
500The default could not be stored.Retry.

Side effects

  • Changes what unnamed agent role references resolve to.
  • Writes an audit record.

Proven by

  • services/nopsai/team_profile_resolution_test.go
  • services/nopsai/team_profile_handlers.go
DELETE/v1/teams/{teamID}/agent-roles/{profileID}Authorized

Deletes a team agent role.

Parameters

NameInTypeRequiredDescription
teamIDpathintegerRequiredTeam identifier.
profileIDpathstringRequiredAgent role to delete.

Call it

Delete an agent roleapi-teams request
curl -sX DELETE -H "Authorization: Bearer $NOPSAI_TOKEN" "$NOPSAI_URL/v1/teams/$TEAM_ID/agent-roles/senior-release-engineer" -w "%{http_code}\n"
Result

Pipelines naming it fail to resolve it on their next run.

Responses

204

Role deleted.

When it fails

StatusCauseWhat to do
500The delete failed.Retry.

Side effects

  • Pipelines referencing the role stop resolving it.
  • Writes an audit record.

Proven by

  • services/nopsai/team_profile_handlers_test.go
  • services/nopsai/team_profile_handlers.go
GET/v1/teams/{teamID}/mcp-profilesAuthorized

Lists a team’s MCP profiles.

Notes

A profile names servers and the tools allowed from them. Pipeline YAML cannot declare a server URL directly, which is why this list is the boundary.

Parameters

NameInTypeRequiredDescription
teamIDpathintegerRequiredTeam identifier.

Call it

List team MCP profilesapi-teams request
curl -s -H "Authorization: Bearer $NOPSAI_TOKEN" "$NOPSAI_URL/v1/teams/$TEAM_ID/mcp-profiles" | jq
Result

The tool sets a pipeline in this team may name in mcp_profiles.

Responses

200application/json

MCP profiles available to the team.

[{
  "name": "jira-readonly",
  "enabled": true,
  "allowed_scopes": ["platform/production"],
  "servers": [{ "server": "jira", "tools": ["search"] }]
}]

When it fails

StatusCauseWhat to do
500The query failed.Platform fault.

Side effects

  • None.

Proven by

  • services/nopsai/team_profile_handlers_test.go
  • services/nopsai/team_profile_handlers.go
POST/v1/teams/{teamID}/mcp-profilesAuthorized

Creates a team MCP profile.

Parameters

NameInTypeRequiredDescription
teamIDpathintegerRequiredTeam identifier.

Call it

Create an MCP profileapi-teams request
curl -sX POST "$NOPSAI_URL/v1/teams/$TEAM_ID/mcp-profiles" \
  -H "Authorization: Bearer $NOPSAI_TOKEN" \
  -H "Content-Type: application/json" \
  --data @profile.json | jq
Result

The created profile, nameable from mcp_profiles in a pipeline, step, or goal task.

Replace before running
  • profile.json names the servers and the tools allowed from each.

Responses

201application/json

Profile created.

{
  "name": "jira-readonly",
  "enabled": true,
  "allowed_scopes": ["platform/production"],
  "servers": [{ "server": "jira", "tools": ["search"] }]
}

When it fails

StatusCauseWhat to do
400An unknown server, an empty tool list, or a scope the team may not use.The message names the field.
500The profile could not be created.Retry.

Side effects

  • Widens what LLM steps in this team can reach.
  • Writes an audit record.

Proven by

  • services/nopsai/team_profile_handlers_test.go
  • services/nopsai/team_profile_handlers.go
GET/v1/teams/{teamID}/mcp-profiles/{profileName}Authorized

Reads one team MCP profile.

Parameters

NameInTypeRequiredDescription
teamIDpathintegerRequiredTeam identifier.
profileNamepathstringRequiredProfile name.

Call it

Read an MCP profileapi-teams request
curl -s -H "Authorization: Bearer $NOPSAI_TOKEN" "$NOPSAI_URL/v1/teams/$TEAM_ID/mcp-profiles/jira-readonly" | jq
Result

The servers and tools the profile permits.

Responses

200application/json

The profile.

{
  "name": "jira-readonly",
  "enabled": true,
  "allowed_scopes": ["platform/production"],
  "servers": [{ "server": "jira", "tools": ["search"] }]
}

When it fails

StatusCauseWhat to do
500The profile could not be loaded.Platform fault.

Side effects

  • None.

Proven by

  • services/nopsai/team_profile_handlers_test.go
  • services/nopsai/team_profile_handlers.go
PUT/v1/teams/{teamID}/mcp-profiles/{profileName}Authorized

Creates or replaces a team MCP profile.

Notes

Profiles are additive at run time: a step cannot narrow what the pipeline granted. Narrowing happens here.

Parameters

NameInTypeRequiredDescription
teamIDpathintegerRequiredTeam identifier.
profileNamepathstringRequiredProfile name.

Call it

Upsert an MCP profileapi-teams request
curl -sX PUT "$NOPSAI_URL/v1/teams/$TEAM_ID/mcp-profiles/jira-readonly" \
  -H "Authorization: Bearer $NOPSAI_TOKEN" \
  -H "Content-Type: application/json" \
  --data @profile.json | jq
Result

The stored profile. Narrowing its tools narrows every pipeline that names it.

Responses

200application/json

Profile stored.

{
  "name": "jira-readonly",
  "enabled": true,
  "allowed_scopes": ["platform/production"],
  "servers": [{ "server": "jira", "tools": ["search"] }]
}

When it fails

StatusCauseWhat to do
400An invalid server or tool list.The message names the field.
500The profile could not be stored.Retry.

Side effects

  • Changes what every pipeline naming this profile can reach.
  • Writes an audit record.

Proven by

  • services/nopsai/team_profile_handlers_test.go
  • services/nopsai/team_profile_handlers.go
DELETE/v1/teams/{teamID}/mcp-profiles/{profileName}Authorized

Deletes a team MCP profile.

Parameters

NameInTypeRequiredDescription
teamIDpathintegerRequiredTeam identifier.
profileNamepathstringRequiredProfile to delete.

Call it

Delete an MCP profileapi-teams request
curl -sX DELETE -H "Authorization: Bearer $NOPSAI_TOKEN" "$NOPSAI_URL/v1/teams/$TEAM_ID/mcp-profiles/jira-readonly" -w "%{http_code}\n"
Result

Pipelines naming it fail authorization on their next run.

Responses

204

Profile deleted.

When it fails

StatusCauseWhat to do
500The delete failed.Retry.

Side effects

  • LLM steps naming the profile lose those tools.
  • Writes an audit record.

Proven by

  • services/nopsai/team_profile_handlers_test.go
  • services/nopsai/team_profile_handlers.go
GET/v1/teams/{teamID}/mcp/profilesAuthorized

Compatibility path for listing team MCP profiles.

Notes

Same handler as /v1/teams/{teamID}/mcp-profiles; prefer the hyphenated form.

POST/v1/teams/{teamID}/mcp/profilesAuthorized

Compatibility path for creating a team MCP profile.

Notes

Same handler as the hyphenated form.

GET/v1/teams/{teamID}/mcp/profiles/{profileName}Authorized

Compatibility path for reading a team MCP profile.

Notes

Same handler as the hyphenated form.

PUT/v1/teams/{teamID}/mcp/profiles/{profileName}Authorized

Compatibility path for replacing a team MCP profile.

Notes

Same handler as the hyphenated form.

DELETE/v1/teams/{teamID}/mcp/profiles/{profileName}Authorized

Compatibility path for deleting a team MCP profile.

Notes

Same handler as the hyphenated form.

How it works

Everything in this area exists so a resource can answer "who owns this?". A team path is not a label — it is the key that access grants, scope resolution, notification routing, and GitOps ownership all join on. That is why moving a team is a bigger operation than renaming one.

The AI resources hang off teams rather than the platform so two teams can hold different models and tool sets without one being able to use the other’s. A profile listed for a team may be inherited rather than defined there, which is why the list can show more than the team owns.

The mcp/profiles paths are compatibility aliases for the hyphenated mcp-profiles routes and reach the same handlers. Prefer the hyphenated form in new integrations.

Implementation evidence

  • services/nopsai/team_handlers.go

    Team, application, defaults, and notification handlers.

  • services/nopsai/team_profile_handlers.go

    Team-owned model, agent role, and MCP profile handlers.

  • pkg/models/types.go

    Team and application record shape.