HLB International Limited · Global Independence System
Version 1.5 · September 2026
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.
GIS has two different addresses, and integrations fail in confusing ways if they are mixed up.
| Address | Example | Use it for |
|---|---|---|
| App domain (people) | https://gis.hlb.globalhttps://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. |
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.
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.
https://<project-ref>.supabase.co/functions/v1/gis-api
All endpoints are prefixed with /v1/.
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 .
Pass your API key in the x-api-key HTTP header with every request:
x-api-key: gis_live_abc123def456...
| Property | Detail |
|---|---|
| Header | x-api-key |
| Scope | Keys are firm-scoped — all operations restricted to your firm's data |
| Permissions | read (GET endpoints) or read + write (GET + POST/PATCH/PUT/DELETE) |
| Rate limit | 100 requests per minute per key (sliding window) |
| Expiry | Optional — keys can have an expiry date set at creation |
| Revocation | Revoked or expired keys return HTTP 401 immediately |
All responses are JSON. Every response includes a meta object with a unique request ID and timestamp.
{
"data": { ... },
"meta": {
"request_id": "550e8400-e29b-41d4-a716-446655440000",
"timestamp": "2026-03-08T12:00:00Z"
}
}
{
"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"
}
}
List endpoints return meta.total, meta.limit, and meta.offset for pagination.
| HTTP Status | Error Code | Description | Resolution |
|---|---|---|---|
| 401 | UNAUTHORIZED | Missing or invalid API key | Check x-api-key header |
| 403 | FORBIDDEN | Key lacks required permission | Request write permission from administrator |
| 404 | ENTITY_NOT_FOUND | Entity not found or belongs to another firm | Verify entity ID |
| 404 | CHECK_NOT_FOUND | Conflict check not found | Verify check ID |
| 429 | RATE_LIMIT_EXCEEDED | Over 100 requests/minute | Implement backoff, retry after 60s |
| 400 | MISSING_FIELDS | Required fields not provided | Check request body |
| 400 | INVALID_BODY | Request body is not valid JSON | Fix JSON syntax |
| 400 | NO_UPDATES | PATCH with no updatable fields | Include at least one field |
| 500 | INTERNAL_ERROR | Server error | Retry; contact support if persistent |
| 500 | QUERY_ERROR | Database query failed | Retry; contact support |
| 500 | CREATE_ERROR | Entity creation failed | Check field values |
| 400 | MISSING_PARAMS | Required query parameters not provided | Supply the documented query parameters |
| 400 | VALIDATION_ERROR | Field-level validation failed | Correct the offending field |
| 400 | INVALID_PIE_STATUS | pie_status was not one of pie, non_pie, unresolved | Send the enum string; booleans are rejected |
| 403 | ACCESS_DENIED | Resource belongs to another firm | Use a record issued by your own firm |
| 403 | FORBIDDEN_STATUS | status was supplied on create or update | Remove status; approvals happen in the GIS UI |
| 404 | NOT_FOUND | Record, identifier, notification, module or job not found | Verify the id or module name |
| 409 | CONFLICT | State conflict, e.g. duplicate identifier | Reconcile the existing record |
| 409 | NOT_EDITABLE | Record is outside its module's editable state | Reopen the record in the GIS UI |
| 409 | PIE_UNRESOLVED | Entity pie_status is unresolved | PATCH the entity to pie or non_pie, then retry |
| 500 | UPDATE_ERROR | Database update failed | Retry; contact support if persistent |
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./v1/entities
Search the firm's entity register. Returns entities owned by the authenticated firm only.
Permission: read
| Parameter | Required | Description |
|---|---|---|
| q | No | Search term (entity name, case-insensitive) |
| jurisdiction | No | Filter by jurisdiction country — display name, ISO code or alias; unrecognised values simply return no rows |
| engagement_type | No | Filter: audit, review, other_assurance, non_assurance, group_member |
| status | No | Filter: active, inactive |
| limit | No | Max results (default 20, max 100) |
| offset | No | Pagination 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 .
{
"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 }
}
/v1/entities/lookup
Find an entity by a specific identifier. Searches across all firms to support cross-firm conflict detection.
Permission: read
| Parameter | Required | Description |
|---|---|---|
| type | Yes | Identifier type (see Data Model section) |
| value | Yes | Identifier 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 .
{
"data": {
"id": "uuid",
"client_name": "Barclays PLC",
"jurisdiction": "United Kingdom",
"engagement_type": "audit",
"pie_status": "pie",
"status": "active",
"own_entity": true,
"identifiers": [ ... ]
}
}
{
"data": {
"id": "uuid",
"client_name": "Barclays PLC",
"jurisdiction": "United Kingdom",
"own_entity": false,
"held_by_firm": "Menzies LLP"
}
}
/v1/entities
Create a new entity in the authenticated firm's register. Automatically triggers duplicate detection.
Permission: write
| Field | Type | Required | Description |
|---|---|---|---|
| client_name | string | Yes | Legal entity name |
| jurisdiction | string | Yes | Country 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_type | string | Yes | See Engagement Types |
| pie_status | enum | No | One of pie, non_pie, unresolved. Omit to default to unresolved. |
| identifiers | array | No | Array of {type, value} objects |
| notes | string | No | Free 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 array alerting you to potential conflicts that should be investigated.
/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 .
/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 .
{
"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.
/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 .
/v1/conflict-checks
Initiate a conflict check for an entity. Broadcasts to all other firms in the network.
Permission: write
| Field | Type | Required | Description |
|---|---|---|---|
| entity_id | UUID | Yes | Entity to check |
| iesba_path | string | Yes | audit_review_4a, other_assurance_4b, or nas_only_component |
| notes | string | No | Notes 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 .
/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 .
{
"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"
}
}
/v1/entities/{id}/identifiers/v1/entities/{id}/identifiers/{identifier_id}/verify/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"
/v1/entities/{id}/parentSets 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"
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./v1/notifications?user_id={uuid}/v1/notifications/{id}/readNotifications 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"
/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"
/v1/compliance/{module}/v1/compliance/{module}/{id}Creates or updates compliance records for five modules: acceptance, nas, breach, gifts, conflict-register.
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.
| Module | Editable status |
|---|---|
| acceptance | draft |
| nas | draft |
| breach | open |
| gifts | pending_review |
| conflict-register | open |
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"
/v1/reports/v1/reports/v1/reports/{id}Reports are generated asynchronously by the report-worker background job (runs every minute, plus a best-effort kick on enqueue).
202 Accepted with a job id and status: "queued".queued → running → completed (or failed after 3 retries).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…" } }
# 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
# 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
# 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
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.
# 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 .
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":{}}}'
apiKey (your firm API key) and, for MCP requests, accessToken (an OAuth 2.1 access token).Request headers at a glance:
| Surface | Method | Required headers |
|---|---|---|
| REST API | GET / POST / PATCH | x-api-key, plus Content-Type: application/json on writes |
| MCP server | POST only | Authorization: Bearer <token>, Content-Type: application/json, Accept: application/json, text/event-stream |
| Field | Type | Description |
|---|---|---|
| id | UUID | Unique identifier |
| client_name | string | Legal entity name |
| jurisdiction | string | Country of registration |
| engagement_type | enum | audit, review, other_assurance, non_assurance, group_member |
| pie_status | enum | pie, non_pie, unresolved (default on create: unresolved) |
| status | enum | active, inactive |
| group_id | string/null | Group identifier (from GLEIF or manual) |
| parent_entity_id | UUID/null | Direct parent entity |
| identifiers | array | Array of identifier objects |
| Type | Description | Example |
|---|---|---|
| lei | Legal Entity Identifier | 213800LBQA1Y9L22JB70 |
| companies_house | UK Companies House number | 01026167 |
| acn | Australian Company Number | 004028077 |
| abn | Australian Business Number | 51004028077 |
| uen | Singapore UEN | 200912345A |
| siren | French SIREN number | 443061841 |
| uid | Swiss UID | CHE-123.456.789 |
| org_nr | Norwegian Org Number | 923609016 |
| cvr | Danish CVR number | 10150817 |
| cro | Irish CRO number | 123456 |
| bis | Israeli Company Number | 520012345 |
| prh | Finnish Business ID | 1234567-8 |
| sec_cik | SEC Central Index Key | 0000320193 |
| brreg | Norwegian Brønnøysund | 923609016 |
| Value | Description |
|---|---|
| audit | Part 4A independence requirements apply |
| review | Part 4A independence requirements apply |
| other_assurance | Part 4B independence requirements apply |
| non_assurance | NAS framework (Sections 600–610) applies if also an audit client |
| group_member | Entity exists for group visibility and conflict detection only |
| Value | Description |
|---|---|
| audit_review_4a | Audit and review engagements |
| other_assurance_4b | Other assurance engagements |
| nas_only_component | NAS or component work where independence may be triggered |
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.
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.
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.
x-api-key or other auth header.All tools operate on your own firm only — the federated data model prevents cross-firm access, including for global administrators.
| Tool | Type | Description |
|---|---|---|
whoami | Read | Confirm the signed-in user, firm and role that the other tools will be scoped to. |
list_my_entities | Read | List entities in your firm, with an optional name filter. |
search_entities | Read | Search the register by name, alternative legal name, or identifier value. |
get_entity | Read | Fetch a single entity including identifiers and parent linkage. |
list_my_conflict_checks | Read | List conflict checks issued by your firm. |
list_my_staff | Read | List staff records held by your firm. |
list_my_staff_declarations | Read | List annual independence declarations and their status. |
list_my_breaches | Read | List recorded independence breaches. |
list_compliance | Read | List records for a compliance module (acceptance, NAS, breach, gifts, conflict register, TCWG, fees, disclosure, declarations, cooling-off). |
get_report_status | Read | Poll a report job and retrieve its download link when complete. |
start_conflict_check | Write | Create an open conflict check. Reviewer action in the GIS interface is still required. |
request_report | Write | Queue an asynchronous report job. |
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
| Symptom | Cause | Fix |
|---|---|---|
| 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 REST | Missing, 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 endpoint | Expected 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 browser | The MCP server accepts POST (Streamable HTTP) only. | None — a 405 confirms the URL is correct. |
| Sign-in loops back to the GIS home page | Consent 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 error | Browser origin is not allow-listed on the backend. | Server-to-server calls are unaffected; contact the HLB Executive Office with the exact origin. |
Review recent API requests in GIS → Settings → API access. The usage section shows requests per day and per week for each key.
| Symptom | Cause | Resolution |
|---|---|---|
| HTTP 401 on every request | Key expired or revoked | Generate a new key in GIS Settings |
| HTTP 403 on POST/PATCH | Key has read-only permission | Ask your administrator to generate a key with write access |
| HTTP 429 intermittently | Rate limit exceeded | Implement exponential backoff; consider batching requests |
| CORS errors in browser | x-api-key not in allowed headers | The API supports x-api-key in CORS; check your frontend configuration |
| Empty results | Firm has no entities or wrong filter | Verify entities exist in GIS; check query parameters |
© 2026 HLB International Limited. This document is confidential and intended for HLB member firm use only.