Platform
ScaiWave ScaiGrid ScaiCore ScaiBot ScaiDrive ScaiKey Models Tools & Services
Solutions
Organisations Developers Internet Service Providers Managed Service Providers AI-in-a-Box
Resources
Support Documentation Blog Downloads
Company
About Research Careers Investment Opportunities Contact
Log in

Global skills

Platform-global (and partner-global) skills reach every eligible scope's resolve response without any per-scope binding. Use for cross-cutting capabilities that every agent should have — a document-authoring skill, a support playbook, a compliance guide.

Shipped 2026-07-03. Three layers: v1 platform-global, v2 partner-global, v3 per-scope opt-out.

v1 — platform-global#

A super_admin marks a skill global and pins the served version. From that moment forward every resolve response — for every tenant, every channel, every user — includes the skill.

bash
1
2
3
4
5
6
7
curl -s -X PUT -H "Authorization: Bearer $SGK_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "enabled": true,
    "version": "0.1.3"
  }' \
  "$SCAIGRID_HOST/v1/modules/scaiskills/skills/scaiscribe/global"

Response:

json
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
{
  "status": "success",
  "data": {
    "slug": "scaiscribe",
    "is_global": true,
    "global_version": "0.1.3",
    "global_scope_type": "platform",
    "global_scope_id": null,
    "updated_at": "2026-07-03T10:15:22Z"
  }
}

Version pinning#

Global skills always serve the pinned version — never latest. This is deliberate: a bad release can't silently propagate to every tenant.

  • Yanking the pinned version un-globals the skill until a super_admin re-pins.
  • Deprecating the skill has the same effect.
  • Only one non-yanked published version per skill can be pinned at a time (enforced in a single transaction).

To move to a new version, call PUT .../global again with the new version — the old pin is cleared and the new one set atomically.

Clearing#

bash
1
2
3
4
5
6
7
8
9
# Either shorthand:
curl -s -X DELETE -H "Authorization: Bearer $SGK_TOKEN" \
  "$SCAIGRID_HOST/v1/modules/scaiskills/skills/scaiscribe/global"

# Or full body:
curl -s -X PUT -H "Authorization: Bearer $SGK_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"enabled": false}' \
  "$SCAIGRID_HOST/v1/modules/scaiskills/skills/scaiscribe/global"

Body-strict schema#

The request body uses additionalProperties: false. A misdirected field returns 422 with the offending field named — deliberately prevents the "silently-ignored path" class of bug:

bash
1
2
3
4
5
curl -s -X PUT -H "Authorization: Bearer $SGK_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"enabled": true, "version": "0.1.3", "path": "typo"}' \
  "$SCAIGRID_HOST/v1/modules/scaiskills/skills/scaiscribe/global"
# → 422 { "error": { "code": "VALIDATION_FAILED", "detail": "path: Extra inputs are not permitted" } }

v2 — partner-global#

Same route, scope_type: "partner" + scope_id: "<partner_id>". Reaches only tenants under the specified partner.

bash
1
2
3
4
5
6
7
8
9
curl -s -X PUT -H "Authorization: Bearer $SGK_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "enabled": true,
    "version": "0.1.3",
    "scope_type": "partner",
    "scope_id": "prt_abc123"
  }' \
  "$SCAIGRID_HOST/v1/modules/scaiskills/skills/vat-verification/global"

Authority:

  • super_admin — can set platform or partner scope for any partner.
  • partner_admin — can set partner scope for their OWN partner (scope_id must equal the caller's partner_id).
  • Any other role — 403.

Response includes the scope:

json
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
{
  "status": "success",
  "data": {
    "slug": "vat-verification",
    "is_global": true,
    "global_version": "0.1.3",
    "global_scope_type": "partner",
    "global_scope_id": "prt_abc123",
    "updated_at": "2026-07-03T10:22:07Z"
  }
}

v3 — per-scope opt-out#

Tenants (or users, or channels) that don't want a specific global can opt out. The global stays live for everyone else; the opting-out scope just stops seeing it.

bash
1
2
3
4
5
6
7
8
curl -s -X PUT -H "Authorization: Bearer $SGK_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "scope_type": "workspace",
    "scope_id": "ws_our_tenant",
    "reason": "conflicts with our internal doc-authoring skill"
  }' \
  "$SCAIGRID_HOST/v1/modules/scaiskills/skills/scaiscribe/global/opt-out"

Authority:

  • super_admin — unrestricted.
  • tenant_admin / partner_admin — can opt out for their OWN workspace.
  • Any user — can opt out for their own user scope.
  • channel / core scope opt-outs require the scaiskills:manage module permission.

Idempotent. Hitting the same (slug, scope) twice returns the same row.

Reversing:

bash
1
2
3
curl -s -X DELETE -H "Authorization: Bearer $SGK_TOKEN" \
  "$SCAIGRID_HOST/v1/modules/scaiskills/skills/scaiscribe/global/opt-out?scope_type=workspace&scope_id=ws_our_tenant"
# → { "status": "success", "data": { "removed": true } }

Returns {"removed": false} (still 200) if there was no opt-out to remove — safe to run in idempotent playbooks.

Opt-outs affect both /resolve AND /mcp/skills/view — a scope can't hide a global from resolve and still fetch its SKILL.md via view. Same guard.

Resolve response — the source field#

Every resolved skill now carries a source field so consumers can badge globals in the preamble:

json
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
{
  "status": "ok",
  "data": {
    "skills": [
      {
        "slug": "scaiscribe",
        "version": "0.1.3",
        "description": "Universal document-authoring skill.",
        "triggers": ["docx", "pptx", "xlsx"],
        "source": "global"
      },
      {
        "slug": "our-legal-template",
        "version": "1.0.0",
        "description": "Company-specific legal boilerplate.",
        "triggers": ["contract", "NDA"],
        "source": "binding"
      }
    ],
    "cache_ttl_ms": 60000
  }
}

Consumers may render "🌐 Global" badges next to source: "global" entries so agent authors know the skill isn't per-tenant configured.

Precedence#

If a scope has BOTH a global and a binding for the same slug, the binding wins. Full order (highest wins):

core > user > channel > workspace > global

This lets a tenant "pin" a different version of a global skill — bind their preferred version to scope_type: "workspace" and it overrides the global for their tenant only.

Cache behavior#

Global-flag changes affect every scope's resolve result. Instead of scanning and invalidating every cache key, the resolver keys its Redis cache under a versioned prefix that bumps on any set_global / clear_global / opt_out / opt_in:

scaiskills:resolve:v{N}:{scope_type}:{scope_id}:p{partner_id_or_-}

Old keys (v{N-1}...) expire naturally at their 60 s TTL. Effect: a super_admin's PUT .../global shows up in every tenant's next resolve within 60 seconds worst case, immediately for scopes that resolve before their old cache expires.

Catalog fields#

GET /v1/modules/scaiskills/skills and GET /skills/{slug} now carry three new fields per row so admin UIs can render "Make global" / "Global" state:

  • is_global (bool)
  • global_version (semver or null)
  • global_scope_type ("platform" | "partner" | null)

Audit#

Every set / clear / opt-out / opt-in writes an audit row with action set to one of:

  • scaiskills.global.set
  • scaiskills.global.clear
  • scaiskills.global.opt_out
  • scaiskills.global.opt_in

Filter the audit log with ?action=scaiskills.global.set to see everything that's been made global platform-wide.

Updated 2026-07-03 14:07:59 View source (.md) rev 1