Skip to main content

Connector Development Guide

Version française : ../../fr/guides/connector-development.md

This guide shows how to write, validate, register, configure, test, synchronise, version and retire a connector to a business system. It is for developers with the developer or tenant_admin role; storing credentials needs tenant_admin or security_officer, and deleting a connector needs tenant_admin.

Concept​

A connector is a manifest of kind Connector that declares a business system, its authentication, its resources and their classification. The Control Plane stores the manifest; the Credential Broker keeps the credentials and hands short leases to the runtime, so an agent never sees a secret. Every outbound call goes through the egress guard and the connector.test or connector.sync policy. A connector is active or disabled.

Manifest fields​

The manifest follows contracts/v1/connector-manifest.schema.json.

FieldTypeRequiredDescription
apiVersion, kind, metadatayesenterprise-ai.io/v1, Connector, name, version, owner.
spec.descriptionstringnoWhat the connector reads or writes.
spec.systemstringyesKind of system, such as rest.
spec.base_urlstringnoBase address, checked by the egress guard when saved.
spec.authobjectyestype (oauth2, api_key, service_account, basic, mtls, none), authorization_url, token_url (required for oauth2), oauth_scopes, credential_ref, delegation (service, on_behalf_of_user).
spec.scopesarraynoScopes the connector may lease.
spec.resourcesarrayyesEach item: name, operations (read, write, delete, subscribe), classification.
spec.syncobjectnomode (none, pull, push, cdc), schedule (cron), incremental.
spec.webhooksobjectnoenabled, signature (hmac_sha256; none is refused), secret_ref (openbao:// or env://).
spec.rate_limitobjectnorequests_per_minute.
spec.data_residencynoRegion where the data must stay.

Create a connector​

The template sdks/python/src/enterprise_ai/templates/connector provides connector.py, connector.yaml (contracts/v1/connector-manifest.schema.json), policies and tests.

enterprise-ai create connector sharepoint
enterprise-ai validate -f sharepoint/connector.yaml

Steps​

  1. Create the project from the template. It writes the manifest, the code, an OPA policy with its tests and pytest tests. In the portal: Developer Studio > Connector studio > New.

    enterprise-ai create connector sharepoint

    Expected result: the files are listed and connector.yaml is marked valid.

    If it fails: exit code 1 with "exists"; add --force or pick another name.

  2. Edit the manifest. Set the system, base_url, the authentication and one entry per resource with its operations and classification. Write only a reference in credential_ref (openbao://...), never a value.

    Expected result: each resource the agents need is declared with the smallest set of operations.

    If it fails: see the field table above.

  3. Validate and test locally. The offline check reads the contract; the tests of the template run the policy and the code.

    enterprise-ai validate -f sharepoint/connector.yaml
    cd sharepoint
    python -m pytest -q tests
    enterprise-ai test authz

    Expected result: exit code 0 everywhere.

    If it fails: fix the fields or the rules listed.

  4. Register the connector. POST /v1/connectors creates it, or updates it when the name exists. In the portal: Save as a new version.

    enterprise-ai apply -f connector.yaml

    Expected result: status created or updated.

    If it fails: 422 invalid_manifest with auth.token_url (OAuth 2 without token address) or webhooks.signature (unsigned webhooks), 403 egress_denied for an address outside the egress policy.

  5. Store the credentials. POST /v1/connectors/{name}/credentials hands them to the Credential Broker; they are written once and never returned. The kind field selects the fields: oauth2_client (client_id, client_secret), oauth2_code (code, redirect_uri, optional code_verifier), api_key (api_key, optional header), service_account (private_key or token), basic (username, password), webhook_secret (secret).

    client.connectors.set_credentials("sharepoint", {"kind": "oauth2_client", "client_id": "<client-id>", "client_secret": "<secret>"})

    Expected result: stored: true with the list of stored field names.

    If it fails: 400 invalid_request lists the missing fields or the allowed kinds; 409 credential_unavailable for oauth2_code before oauth2_client.

  6. Test the connection. POST /v1/connectors/{name}/test resolves the credentials and calls base_url through the egress guard. In the portal: Connector studio > connector > Test > Test the connection.

    Expected result: ok: true, credentials: ok, the http_status and latency_ms.

    If it fails: error gives the cause: egress_denied, upstream_unavailable, credential_unavailable or credential_rejected.

  7. Synchronise. POST /v1/connectors/{name}/sync queues a sync run (202) on the readable resources; an incremental sync resumes from the last cursor unless full is set. GET /v1/connectors/{name}/sync-runs lists the runs.

    client.connectors.sync("sharepoint", full=True)

    Expected result: a sync run in queued, then running and succeeded.

    If it fails: 409 sync_disabled (sync.mode is none), 409 connector_disabled, 409 sync_in_progress, 400 invalid_request for a resource that is not readable.

  8. Publish a new version. Raise metadata.version and apply the manifest again; the stored credentials are kept.

    Expected result: status updated.

    If it fails: test the connection again (step 6) after a change of auth.

  9. Disable or delete. PATCH /v1/connectors/{name} with status: disabled stops new leases and revokes the active ones; DELETE /v1/connectors/{name} also deletes the stored secrets.

    client.connectors.update("sharepoint", status="disabled")

    Expected result: status disabled, or 204 on delete.

    If it fails: 404 connector_not_found.

warning

Deleting a connector deletes its credentials from the secret store. Disable it first when you may need it again.

OAuth, API keys and secrets​

Credentials (OAuth client secret, API key of a business service) are write only: POST /v1/connectors/{name}/credentials hands them to the Credential Broker and they are never returned nor exposed to agents. Generic secrets go through POST /v1/secrets.

client.connectors.set_credentials("sharepoint", {"kind": "oauth2_client", "client_id": "<client-id>", "client_secret": "<secret>"})

At run time the runtime asks POST /v1/credentials/broker/lease for a lease (30 to 3600 seconds) limited to scopes of the connector. Webhooks of a connector arrive on POST /v1/webhooks/{connector}, signed with HMAC-SHA256 and a timestamp within 5 minutes.

Test and sync​

POST /v1/connectors creates the connector, POST /v1/connectors/{name}/test checks the connection, POST /v1/connectors/{name}/sync starts a sync.

Troubleshooting​

SymptomCauseFix
404 connector_not_foundUnknown name in the tenant.Check with enterprise-ai get connectors.
422 invalid_manifestauth.token_url missing for oauth2, or webhooks.signature: none.Add the token address; keep hmac_sha256.
403 egress_deniedbase_url, token_url or authorization_url is outside the egress policy.Ask the administrator to allow the host.
400 invalid_requestUnknown credential kind or missing fields.Send the fields of the kind listed in step 5.
502 credential_unavailableNo credential stored, or the secret reference cannot be resolved.Store the credentials, check credential_ref.
502 credential_rejectedThe token endpoint refused the client.Check the client id and secret with the system owner.
409 sync_disabled, sync_in_progress, connector_disabledSync not configured, already running, or connector disabled.Set sync.mode, wait for the run, or enable the connector.
403 scope_not_allowedThe lease asks for a scope outside spec.scopes.Add the scope to the manifest or ask for less.
401 invalid_leaseThe lease is expired or revoked.Ask for a new lease.
401 invalid_signature, webhook_replayWebhook signature wrong or timestamp too old.Sign with the stored webhook secret and the current time.

Next steps​