GIS Integration API — Developer Guide

HLB International Limited · Global Independence System

Version 1.5 · September 2026

Contents
  1. Introduction
  2. Which URL to share
  3. Getting Started
  4. Authentication
  5. Response Format
  6. Error Codes Reference
  7. Endpoints — Detailed Reference
  8. Integration Patterns
  9. Request Examples (curl and Postman)
  10. Data Model Reference
  11. MCP Server (AI Assistant Integration)
  12. Security Best Practices
  13. Support & Troubleshooting

1. Introduction

The GIS Integration API enables firm CRM and practice management systems to exchange entity data and compliance status with the Global Independence System (GIS). Rather than building bespoke connectors to each firm's systems, GIS exposes a standard REST API that firms can integrate from their side.

Integration model: Firm systems connect to GIS via REST API. GIS does not connect to firm systems.

Who this guide is for: Firm IT teams, system integrators, and CRM administrators building integrations with GIS.

1a. Which URL to share

GIS has two different addresses, and integrations fail in confusing ways if they are mixed up.

AddressExampleUse it for
App domain (people) https://gis.hlb.global
https://gis.hlb.global/docs/gis-api-developer-guide.html
Signing in, the GIS interface, this guide, and the OAuth consent screen your browser is redirected to.
Backend URL (machines) https://<project-ref>.supabase.co/functions/v1/gis-api/v1/
https://<project-ref>.supabase.co/functions/v1/mcp
Every REST API call and every MCP client connection.
Never share the app domain as an API or MCP endpoint. URLs such as https://gis.hlb.global/functions/v1/mcp or https://gis.hlb.global/functions/v1/gis-api/v1/entities are answered by the GIS web application, which returns its HTML page with HTTP 200 instead of a 404. Clients then report a parse error, “unexpected token <”, or “not a valid MCP server”. The OAuth 2.1 metadata is also bound to the backend host, so the URL cannot simply be rewritten to the custom domain.

The exact backend URL for your environment, plus one-click Copy API endpoint and Copy MCP endpoint buttons and a connection URL validator, are in GIS → Settings → API Documentation. Copy from there rather than typing the URL by hand.

2. Getting Started

Requesting an API Key

API keys are generated by your firm administrator in GIS under Settings → API access. Global administrators can also generate keys for any firm. Contact your firm administrator or the HLB Executive Office to request a key.

API Key Security

Base URL

https://<project-ref>.supabase.co/functions/v1/gis-api

All endpoints are prefixed with /v1/.

Quick Test

curl -s -H "x-api-key: gis_live_YOUR_KEY_HERE" \
  "https://<project-ref>.supabase.co/functions/v1/gis-api/v1/entities?limit=1" \
  | jq .

3. Authentication

Pass your API key in the x-api-key HTTP header with every request:

x-api-key: gis_live_abc123def456...
PropertyDetail
Headerx-api-key
ScopeKeys are firm-scoped — all operations restricted to your firm's data
Permissionsread (GET endpoints) or read + write (GET + POST/PATCH/PUT/DELETE)
Rate limit100 requests per minute per key (sliding window)
ExpiryOptional — keys can have an expiry date set at creation
RevocationRevoked or expired keys return HTTP 401 immediately

4. Response Format

All responses are JSON. Every response includes a meta object with a unique request ID and timestamp.

Success (200)

{
  "data": { ... },
  "meta": {
    "request_id": "550e8400-e29b-41d4-a716-446655440000",
    "timestamp": "2026-03-08T12:00:00Z"
  }
}

Error (4xx / 5xx)

{
  "error": {
    "code": "ENTITY_NOT_FOUND",
    "message": "No entity found with the specified identifier."
  },
  "meta": {
    "request_id": "550e8400-e29b-41d4-a716-446655440000",
    "timestamp": "2026-03-08T12:00:00Z"
  }
}

Pagination

List endpoints return meta.total, meta.limit, and meta.offset for pagination.

5. Error Codes Reference

HTTP StatusError CodeDescriptionResolution
401UNAUTHORIZEDMissing or invalid API keyCheck x-api-key header
403FORBIDDENKey lacks required permissionRequest write permission from administrator
404ENTITY_NOT_FOUNDEntity not found or belongs to another firmVerify entity ID
404CHECK_NOT_FOUNDConflict check not foundVerify check ID
429RATE_LIMIT_EXCEEDEDOver 100 requests/minuteImplement backoff, retry after 60s
400MISSING_FIELDSRequired fields not providedCheck request body
400INVALID_BODYRequest body is not valid JSONFix JSON syntax
400NO_UPDATESPATCH with no updatable fieldsInclude at least one field
500INTERNAL_ERRORServer errorRetry; contact support if persistent
500QUERY_ERRORDatabase query failedRetry; contact support
500CREATE_ERROREntity creation failedCheck field values
400MISSING_PARAMSRequired query parameters not providedSupply the documented query parameters
400VALIDATION_ERRORField-level validation failedCorrect the offending field
400INVALID_PIE_STATUSpie_status was not one of pie, non_pie, unresolvedSend the enum string; booleans are rejected
403ACCESS_DENIEDResource belongs to another firmUse a record issued by your own firm
403FORBIDDEN_STATUSstatus was supplied on create or updateRemove status; approvals happen in the GIS UI
404NOT_FOUNDRecord, identifier, notification, module or job not foundVerify the id or module name
409CONFLICTState conflict, e.g. duplicate identifierReconcile the existing record
409NOT_EDITABLERecord is outside its module's editable stateReopen the record in the GIS UI
409PIE_UNRESOLVEDEntity pie_status is unresolvedPATCH the entity to pie or non_pie, then retry
500UPDATE_ERRORDatabase update failedRetry; contact support if persistent
PIE must be resolved before independence workflows. POST /v1/conflict-checks and POST /v1/compliance/acceptance return 409 PIE_UNRESOLVED when the target entity's pie_status is unresolved. Call PATCH /v1/entities/{id} with {"pie_status":"pie"} or {"pie_status":"non_pie"} first, then retry.

6. Endpoints — Detailed Reference

6.1 Search Entities

GET /v1/entities

Search the firm's entity register. Returns entities owned by the authenticated firm only.

Permission: read

ParameterRequiredDescription
qNoSearch term (entity name, case-insensitive)
jurisdictionNoFilter by jurisdiction country — display name, ISO code or alias; unrecognised values simply return no rows
engagement_typeNoFilter: audit, review, other_assurance, non_assurance, group_member
statusNoFilter: active, inactive
limitNoMax results (default 20, max 100)
offsetNoPagination offset
curl -s -H "x-api-key: gis_live_abc123" \
  "https://<project-ref>.supabase.co/functions/v1/gis-api/v1/entities?q=barclays&limit=5" \
  | jq .

Example Response

{
  "data": [
    {
      "id": "550e8400-e29b-41d4-a716-446655440000",
      "client_name": "Barclays PLC",
      "jurisdiction": "United Kingdom",
      "engagement_type": "audit",
      "pie_status": "pie",
      "status": "active",
      "group_id": "GLEIF_213800",
      "parent_entity_id": null,
      "identifiers": [
        { "type": "lei", "value": "213800LBQA1Y9L22JB70", "verified": true },
        { "type": "companies_house", "value": "01026167", "verified": true }
      ],
      "created_at": "2026-01-15T10:30:00Z",
      "updated_at": "2026-03-01T14:22:00Z"
    }
  ],
  "meta": { "total": 142, "limit": 5, "offset": 0 }
}

6.2 Lookup Entity by Identifier

GET /v1/entities/lookup

Find an entity by a specific identifier. Searches across all firms to support cross-firm conflict detection.

Permission: read

Cross-firm visibility: For entities belonging to other firms, only name, jurisdiction, and holding firm name are returned. Engagement type, PIE status, and other details are not disclosed.
ParameterRequiredDescription
typeYesIdentifier type (see Data Model section)
valueYesIdentifier value
curl -s -H "x-api-key: gis_live_abc123" \
  "https://<project-ref>.supabase.co/functions/v1/gis-api/v1/entities/lookup?type=lei&value=213800LBQA1Y9L22JB70" \
  | jq .

Response — Own Firm's Entity

{
  "data": {
    "id": "uuid",
    "client_name": "Barclays PLC",
    "jurisdiction": "United Kingdom",
    "engagement_type": "audit",
    "pie_status": "pie",
    "status": "active",
    "own_entity": true,
    "identifiers": [ ... ]
  }
}

Response — Another Firm's Entity

{
  "data": {
    "id": "uuid",
    "client_name": "Barclays PLC",
    "jurisdiction": "United Kingdom",
    "own_entity": false,
    "held_by_firm": "Menzies LLP"
  }
}

6.3 Create Entity

POST /v1/entities

Create a new entity in the authenticated firm's register. Automatically triggers duplicate detection.

Permission: write

FieldTypeRequiredDescription
client_namestringYesLegal entity name
jurisdictionstringYesCountry of registration. Send the English country name as shown in the GIS picker (e.g. United Kingdom); ISO alpha-2/alpha-3 codes and common aliases are also accepted on write and normalised to the display name. An unrecognised value returns 400 JURISDICTION_NOT_RECOGNISED.
engagement_typestringYesSee Engagement Types
pie_statusenumNoOne of pie, non_pie, unresolved. Omit to default to unresolved.
identifiersarrayNoArray of {type, value} objects
notesstringNoFree text notes
curl -s -X POST -H "x-api-key: gis_live_abc123" \
  -H "Content-Type: application/json" \
  -d '{
    "client_name": "Acme Holdings Ltd",
    "jurisdiction": "United Kingdom",
    "engagement_type": "audit",
    "pie_status": "non_pie",
    "identifiers": [
      { "type": "companies_house", "value": "12345678" }
    ],
    "notes": "Imported from CRM ref: ACC-2026-001"
  }' \
  "https://<project-ref>.supabase.co/functions/v1/gis-api/v1/entities" \
  | jq .
Duplicate warnings: The entity is created regardless of duplicates (matching the GIS UI behaviour), but the response includes a duplicate_warnings array alerting you to potential conflicts that should be investigated.

6.4 Update Entity

PATCH /v1/entities/{id}

Update an entity owned by the authenticated firm. Only provided fields are updated.

Permission: write

Updatable fields: client_name, jurisdiction, engagement_type, pie_status, status, notes, relationship_type, parent_entity_id

curl -s -X PATCH -H "x-api-key: gis_live_abc123" \
  -H "Content-Type: application/json" \
  -d '{ "engagement_type": "review", "pie_status": "pie" }' \
  "https://<project-ref>.supabase.co/functions/v1/gis-api/v1/entities/550e8400-..." \
  | jq .
Only entities belonging to your firm can be updated. Attempting to update another firm's entity returns HTTP 404.

6.5 Get Compliance Status

GET /v1/entities/{id}/compliance

Returns a compliance summary for an entity with per-module traffic light statuses.

Permission: read

curl -s -H "x-api-key: gis_live_abc123" \
  "https://<project-ref>.supabase.co/functions/v1/gis-api/v1/entities/550e8400-.../compliance" \
  | jq .

Example Response

{
  "data": {
    "entity_id": "uuid",
    "client_name": "Barclays PLC",
    "pie_status": "pie",
    "overall_status": "attention_required",
    "modules": {
      "conflict_checks": { "status": "clear", "active_checks": 0 },
      "acceptance": { "status": "approved", "approved_at": "2026-01-20T14:30:00Z" },
      "nas_evaluations": { "status": "clear", "active_count": 0, "prohibited_count": 0 },
      "fee_dependency": { "status": "warning", "dependency_percentage": 18.5 },
      "rotation": {
        "status": "attention_required",
        "roles_nearing_rotation": [
          { "role": "engagement_partner", "due_date": "2026-09-30" }
        ]
      },
      "breaches": { "status": "clear", "open_count": 0 },
      "declarations": { "status": "clear", "pending_count": 0, "overdue_count": 0 }
    }
  }
}

The overall_status is derived: if any module is attention_required or in breach, the overall is attention_required. If all are clear or approved, the overall is clear.

6.6 Get Group Structure

GET /v1/entities/{id}/group

Returns the group structure for an entity — parent, subsidiaries, and group metadata. Cross-firm entities show firm name but not engagement details.

Permission: read

curl -s -H "x-api-key: gis_live_abc123" \
  "https://<project-ref>.supabase.co/functions/v1/gis-api/v1/entities/550e8400-.../group" \
  | jq .

6.7 Initiate Conflict Check

POST /v1/conflict-checks

Initiate a conflict check for an entity. Broadcasts to all other firms in the network.

Permission: write

FieldTypeRequiredDescription
entity_idUUIDYesEntity to check
iesba_pathstringYesaudit_review_4a, other_assurance_4b, or nas_only_component
notesstringNoNotes for responding firms
curl -s -X POST -H "x-api-key: gis_live_abc123" \
  -H "Content-Type: application/json" \
  -d '{
    "entity_id": "550e8400-e29b-41d4-a716-446655440000",
    "iesba_path": "audit_review_4a",
    "notes": "Pre-engagement check from CRM"
  }' \
  "https://<project-ref>.supabase.co/functions/v1/gis-api/v1/conflict-checks" \
  | jq .

6.8 Get Conflict Check Status

GET /v1/conflict-checks/{id}

Returns the status of a previously initiated conflict check with response summary.

Permission: read

curl -s -H "x-api-key: gis_live_abc123" \
  "https://<project-ref>.supabase.co/functions/v1/gis-api/v1/conflict-checks/550e8400-..." \
  | jq .

Example Response

{
  "data": {
    "id": "uuid",
    "entity_name": "Acme Holdings Ltd",
    "status": "complete",
    "issued_at": "2026-03-08T10:00:00Z",
    "responses": {
      "total": 5,
      "received": 5,
      "no_conflict": 4,
      "potential_conflict": 1,
      "conflict_identified": 0
    },
    "resolution": "approved",
    "resolved_at": "2026-03-08T16:30:00Z"
  }
}

6.9 Identifiers — Add, Verify, Delete

POST /v1/entities/{id}/identifiers
POST /v1/entities/{id}/identifiers/{identifier_id}/verify
DELETE /v1/entities/{id}/identifiers/{identifier_id}

Manage entity identifiers (LEI, Companies House, RCS, VAT, ACRA UEN, etc.). Duplicate identifier values within the firm return 409 CONFLICT.

Body fields: type, value and jurisdiction are all required; source is optional.

Permission: write

curl -X POST -H "x-api-key: gis_live_abc123" -H "Content-Type: application/json" \
  -d '{"type":"lei","value":"529900T8BM49AURSDO55","jurisdiction":"United Kingdom","source":"crm-sync"}' \
  "https://<project-ref>.supabase.co/functions/v1/gis-api/v1/entities/{id}/identifiers"

6.10 Set Entity Parent (Group Structure)

PATCH /v1/entities/{id}/parent

Sets or clears the parent of an entity. Cross-firm parents are only allowed when both entities belong to the same group (enforced by the set_entity_parent RPC).

Permission: write

curl -X PATCH -H "x-api-key: gis_live_abc123" -H "Content-Type: application/json" \
  -d '{"parent_entity_id":"550e8400-..."}' \
  "https://<project-ref>.supabase.co/functions/v1/gis-api/v1/entities/{id}/parent"
Omitting parent_entity_id clears the parent. The handler treats a missing field as null, so always send the field explicitly — send null only when you intend to detach the entity from its parent.

6.11 Notifications (Per-User)

GET /v1/notifications?user_id={uuid}
POST /v1/notifications/{id}/read

Notifications are per-user. The user_id query parameter is required and must belong to the firm that owns the API key. Optional unread_only=true filter.

Permission: read (list), write (mark read)

curl -H "x-api-key: gis_live_abc123" \
  "https://<project-ref>.supabase.co/functions/v1/gis-api/v1/notifications?user_id={uuid}&unread_only=true"

6.12 Compliance — Read

GET /v1/compliance/{module}

Reads compliance records for a module. Supports entity_id and status filters plus standard pagination.

Modules: acceptance, nas, fee, declarations, rotation, conflict-register, gifts, breach, tcwg, disclosure

Permission: read

curl -H "x-api-key: gis_live_abc123" \
  "https://<project-ref>.supabase.co/functions/v1/gis-api/v1/compliance/breach?status=open&limit=25"

6.13 Compliance — Write (Draft Only)

POST /v1/compliance/{module}
PATCH /v1/compliance/{module}/{id}

Creates or updates compliance records for five modules: acceptance, nas, breach, gifts, conflict-register.

Editable-state allow-list. A record can only be updated while it sits in its module's working state. Anything else returns 409 NOT_EDITABLE. Any client-supplied status, on create or update, returns 403 FORBIDDEN_STATUS — approval and finalisation happen in the GIS UI by a reviewer, and the API cannot bypass that control.
ModuleEditable status
acceptancedraft
nasdraft
breachopen
giftspending_review
conflict-registeropen

Creating an acceptance record against an entity whose pie_status is unresolved returns 409 PIE_UNRESOLVED; resolve the entity's PIE status first. A pie_status supplied in the body must be the enum pie | non_pie | unresolved — booleans return 400 INVALID_PIE_STATUS.

Permission: write

Server-derived fields: firm_id and the actor column (initiated_by, reported_by, created_by, etc.) are set from the API key — do not pass them.

curl -X POST -H "x-api-key: gis_live_abc123" -H "Content-Type: application/json" \
  -d '{"entity_id":"...","breach_type":"independence","description":"…","occurred_on":"2026-04-20"}' \
  "https://<project-ref>.supabase.co/functions/v1/gis-api/v1/compliance/breach"

6.14 Reports — Async Job Pattern

POST /v1/reports
GET /v1/reports
GET /v1/reports/{id}

Reports are generated asynchronously by the report-worker background job (runs every minute, plus a best-effort kick on enqueue).

  1. POST a report request — returns 202 Accepted with a job id and status: "queued".
  2. GET the job by id every 5–10 seconds. Status transitions: queued → running → completed (or failed after 3 retries).
  3. When status = "completed", the response includes a download_url — a 1-hour signed URL to the report file in the report-exports bucket.

Body: report_type (required), format (pdf | csv | xlsx | json, default json) and filters (optional object, persisted on the job and passed to the generator).

Report types: conflict_check_summary, network_response, entity_register, audit_activity, declaration_completion, rotation_status, fee_dependency, breach_summary

Permission: read

# 1. Enqueue
curl -X POST -H "x-api-key: gis_live_abc123" -H "Content-Type: application/json" \
  -d '{"report_type":"breach_summary","format":"csv","filters":{"from":"2026-01-01","to":"2026-04-30"}}' \
  "https://<project-ref>.supabase.co/functions/v1/gis-api/v1/reports"

# 2. Poll
curl -H "x-api-key: gis_live_abc123" \
  "https://<project-ref>.supabase.co/functions/v1/gis-api/v1/reports/{job_id}"
# → { "data": { "status": "completed", "download_url": "https://…signed…" } }

7. Integration Patterns

7.1 CRM Entity Sync (Push)

# On new client created in CRM:
1. POST /v1/entities (create in GIS)
2. Store returned entity_id in CRM as foreign key
3. Check duplicate_warnings — flag for review if matches found

# On client updated in CRM:
1. PATCH /v1/entities/{id} (update in GIS)

# Nightly sync:
1. GET /v1/entities?limit=100&offset=0 (paginate through all)
2. Compare with CRM records, reconcile differences

7.2 Pre-Engagement Independence Check

# When engagement is proposed in CRM:
1. GET /v1/entities/lookup?type=companies_house&value={number}
2. If entity exists:
   a. GET /v1/entities/{id}/compliance → check traffic light
   b. If clear → proceed
   c. If attention_required → flag for manual review
3. If entity not found:
   a. POST /v1/entities (create)
   b. POST /v1/conflict-checks (initiate check)
   c. Poll GET /v1/conflict-checks/{id} until complete
   d. Proceed or escalate based on result

7.3 Dashboard Widget (Read-Only)

# For each audit client displayed in CRM dashboard:
1. GET /v1/entities/{id}/compliance
2. Display overall_status as traffic light (green/amber/red)
3. Link "View in GIS" to the GIS application

7a. Request Examples (curl and Postman)

All examples use the backend URL. Substitute <project-ref> with the value shown in GIS → Settings → API Documentation, where the exact URLs can also be copied with one click.

curl — REST API

# 1. Confirm the key, firm binding and rate limit
curl -s -H "x-api-key: gis_live_YOUR_KEY_HERE" \
  "https://<project-ref>.supabase.co/functions/v1/gis-api/v1/health" | jq .

# 2. Search the firm's entity register
curl -s -H "x-api-key: gis_live_YOUR_KEY_HERE" \
  "https://<project-ref>.supabase.co/functions/v1/gis-api/v1/entities?q=acme&limit=5" | jq .

# 3. Create an entity
curl -s -X POST "https://<project-ref>.supabase.co/functions/v1/gis-api/v1/entities" \
  -H "x-api-key: gis_live_YOUR_KEY_HERE" \
  -H "Content-Type: application/json" \
  -d '{
        "client_name": "Acme Holdings Ltd",
        "jurisdiction": "United Kingdom",
        "engagement_type": "audit",
        "pie_status": "non_pie"
      }' | jq .

curl — MCP server

MCP is JSON-RPC over Streamable HTTP: always POST, and always send Accept: application/json, text/event-stream — omitting it returns 406 Not Acceptable. curl is for diagnostics; day-to-day use goes through an MCP client that performs the OAuth 2.1 flow for you.

# 1. Unauthenticated call — expected: 401 with a WWW-Authenticate pointer
curl -i -X POST "https://<project-ref>.supabase.co/functions/v1/mcp" \
  -H "Content-Type: application/json" \
  -H "Accept: application/json, text/event-stream" \
  -d '{"jsonrpc":"2.0","id":1,"method":"initialize",
       "params":{"protocolVersion":"2025-06-18","capabilities":{},
                 "clientInfo":{"name":"curl","version":"1.0"}}}'

# 2. Protected-resource document (no auth required)
curl -s "https://<project-ref>.supabase.co/functions/v1/mcp/.well-known/oauth-protected-resource" | jq .

# 3. List tools with an OAuth access token
curl -s -X POST "https://<project-ref>.supabase.co/functions/v1/mcp" \
  -H "Authorization: Bearer $ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -H "Accept: application/json, text/event-stream" \
  -d '{"jsonrpc":"2.0","id":2,"method":"tools/list","params":{}}'

# 4. Call a tool
curl -s -X POST "https://<project-ref>.supabase.co/functions/v1/mcp" \
  -H "Authorization: Bearer $ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -H "Accept: application/json, text/event-stream" \
  -d '{"jsonrpc":"2.0","id":3,"method":"tools/call",
       "params":{"name":"whoami","arguments":{}}}'

Postman

  1. In GIS, open Settings → API Documentation and use Copy Postman collection; the backend URL for your environment is already filled in.
  2. In Postman choose Import → Raw text and paste the collection.
  3. Set the collection variables: apiKey (your firm API key) and, for MCP requests, accessToken (an OAuth 2.1 access token).
  4. Postman does not perform Dynamic Client Registration; obtain the MCP token from an MCP client, or configure Postman's OAuth 2.0 helper using the discovery document above.

Request headers at a glance:

SurfaceMethodRequired headers
REST APIGET / POST / PATCHx-api-key, plus Content-Type: application/json on writes
MCP serverPOST onlyAuthorization: Bearer <token>, Content-Type: application/json, Accept: application/json, text/event-stream

8. Data Model Reference

Entity Fields

FieldTypeDescription
idUUIDUnique identifier
client_namestringLegal entity name
jurisdictionstringCountry of registration
engagement_typeenumaudit, review, other_assurance, non_assurance, group_member
pie_statusenumpie, non_pie, unresolved (default on create: unresolved)
statusenumactive, inactive
group_idstring/nullGroup identifier (from GLEIF or manual)
parent_entity_idUUID/nullDirect parent entity
identifiersarrayArray of identifier objects

Identifier Types

TypeDescriptionExample
leiLegal Entity Identifier213800LBQA1Y9L22JB70
companies_houseUK Companies House number01026167
acnAustralian Company Number004028077
abnAustralian Business Number51004028077
uenSingapore UEN200912345A
sirenFrench SIREN number443061841
uidSwiss UIDCHE-123.456.789
org_nrNorwegian Org Number923609016
cvrDanish CVR number10150817
croIrish CRO number123456
bisIsraeli Company Number520012345
prhFinnish Business ID1234567-8
sec_cikSEC Central Index Key0000320193
brregNorwegian Brønnøysund923609016

Engagement Types

ValueDescription
auditPart 4A independence requirements apply
reviewPart 4A independence requirements apply
other_assurancePart 4B independence requirements apply
non_assuranceNAS framework (Sections 600–610) applies if also an audit client
group_memberEntity exists for group visibility and conflict detection only

IESBA Path Values

ValueDescription
audit_review_4aAudit and review engagements
other_assurance_4bOther assurance engagements
nas_only_componentNAS or component work where independence may be triggered

9. MCP Server (AI Assistant Integration)

GIS exposes a Model Context Protocol server so AI assistants and agentic clients (Claude, ChatGPT, Cursor, n8n, custom workflows) can work with GIS as MCP tools — same firm scoping and the same audit trail as the REST API.

Authentication changed in July 2026. The MCP server no longer uses API keys. The old gis-mcp endpoint and the gis_mcp_… key type were retired on 7 July 2026, and the Settings → MCP Keys screen was removed. The current server authenticates with OAuth 2.1: your AI client registers itself automatically (Dynamic Client Registration), you sign in to GIS with your normal account, and you approve the connection on a consent screen. The tools then act as you, with your role and firm scoping enforced by the database.

Endpoint

https://<project-ref>.supabase.co/functions/v1/mcp

Copy the exact URL for your environment from GIS → Settings → API access. Requests without a valid bearer token return 401 with a WWW-Authenticate header pointing at the OAuth discovery document — MCP clients follow that automatically, so in practice you only ever paste the URL above.

Connecting a client

  1. In your AI client, add a new MCP server of type HTTP / Streamable HTTP (Claude: Settings → Connectors → Add custom connector; Cursor: Settings → MCP → Add new server; n8n: MCP Client node).
  2. Paste the endpoint URL. Do not add any x-api-key or other auth header.
  3. The client opens a GIS sign-in page. Sign in with your normal GIS account (including MFA if enabled).
  4. Approve the connection on the consent screen. The client stores the token and refreshes it automatically.
  5. You should now see the GIS tools listed in the client. Test with: "Use the GIS tools to tell me who I am and list five of my firm's entities."

Tools

All tools operate on your own firm only — the federated data model prevents cross-firm access, including for global administrators.

ToolTypeDescription
whoamiReadConfirm the signed-in user, firm and role that the other tools will be scoped to.
list_my_entitiesReadList entities in your firm, with an optional name filter.
search_entitiesReadSearch the register by name, alternative legal name, or identifier value.
get_entityReadFetch a single entity including identifiers and parent linkage.
list_my_conflict_checksReadList conflict checks issued by your firm.
list_my_staffReadList staff records held by your firm.
list_my_staff_declarationsReadList annual independence declarations and their status.
list_my_breachesReadList recorded independence breaches.
list_complianceReadList records for a compliance module (acceptance, NAS, breach, gifts, conflict register, TCWG, fees, disclosure, declarations, cooling-off).
get_report_statusReadPoll a report job and retrieve its download link when complete.
start_conflict_checkWriteCreate an open conflict check. Reviewer action in the GIS interface is still required.
request_reportWriteQueue an asynchronous report job.
MCP cannot approve regulated artefacts. Write tools deliberately produce draft / open records only — final approval, escalation and rejection happen exclusively in the GIS interface by an authorised reviewer.

Permissions and auditing

Example AI Workflow — Pre-engagement Independence Check

Agent prompt:
  "We're considering a new audit engagement with Acme Holdings Ltd in the UK.
   Check if we already have them, list any open compliance items, and start
   a conflict check if we don't already have an active one."

The agent will autonomously:
  1. whoami
  2. search_entities  { query: "Acme Holdings" }
  3. get_entity       { entity_id: <match> }                     # if found
  4. list_compliance  { module: "breach", entity_id, status: "open" }
  5. start_conflict_check { entity_name: "Acme Holdings Ltd",
                            jurisdiction: "United Kingdom",
                            engagement_type: "audit_review",
                            notes: "..." }                          # if needed

10. Security Best Practices

11. Support & Troubleshooting

Common connection errors

SymptomCauseFix
Client returns HTML, “unexpected token <”, or “not a valid MCP server”The URL uses the app domain (gis.hlb.global) with a /functions/v1/… path, which the web app answers with its HTML shell.Use https://<project-ref>.supabase.co/functions/v1/mcp (or /gis-api/v1/ for REST).
404 opening this guide/public included in the path.https://gis.hlb.global/docs/gis-api-developer-guide.html
401 UNAUTHORIZED from RESTMissing, mistyped or revoked x-api-key, or a key belonging to another firm.Verify with GET /v1/health; request a new key from your firm administrator.
401 from the MCP endpointExpected before sign-in — the response carries a WWW-Authenticate pointer.None; the MCP client follows it and starts OAuth automatically.
405 Method Not Allowed when opening the MCP URL in a browserThe MCP server accepts POST (Streamable HTTP) only.None — a 405 confirms the URL is correct.
Sign-in loops back to the GIS home pageConsent not completed in the same browser session.Sign in to GIS in that browser (including MFA), then retry the connection and approve on the consent screen.
“Failed to fetch” / CORS errorBrowser origin is not allow-listed on the backend.Server-to-server calls are unaffected; contact the HLB Executive Office with the exact origin.

Checking Request History

Review recent API requests in GIS → Settings → API access. The usage section shows requests per day and per week for each key.

Common Issues

SymptomCauseResolution
HTTP 401 on every requestKey expired or revokedGenerate a new key in GIS Settings
HTTP 403 on POST/PATCHKey has read-only permissionAsk your administrator to generate a key with write access
HTTP 429 intermittentlyRate limit exceededImplement exponential backoff; consider batching requests
CORS errors in browserx-api-key not in allowed headersThe API supports x-api-key in CORS; check your frontend configuration
Empty resultsFirm has no entities or wrong filterVerify entities exist in GIS; check query parameters

Contact


© 2026 HLB International Limited. This document is confidential and intended for HLB member firm use only.