Code sync
The code sync routes keep a repository in the code graph. They need the fabric:codesync.write grant and a project-scoped credential, except names.
curl -sS -H "Authorization: Bearer $FABRIC_API_KEY" \
https://fabric.wexa.ai/v1/codesync/names{ "org_id": "org_abc", "dept_id": "dept_abc", "project_id": "proj_abc", "user_id": "user_abc" }Parameters
| Parameter | Type | Required | Notes |
|---|---|---|---|
repo_url | string | Required | POST /v1/codesync/bootstrap: the clone URL, matched against the webhook payload's clone_url. |
repo_name | string | Required | bootstrap: the owner/name form, matched against the payload's full_name. |
ttl_seconds | integer | Optional | bootstrap: how long the binding stays valid. |
connector_id, nodes, edges | body | Optional | POST /v1/codesync/ingest-event and bulk-ingest: a client-shaped body, forwarded as sent. A body over the size cap is refused, not truncated. |
job_id | string | Required | GET /v1/codesync/ingest-status/{job_id}: the job ingest-async returned. Poll on an interval; it counts against quota. |
| none | names, connector and bolt-credentials take no parameters. |
Errors
| Status | Code | Raised when |
|---|---|---|
| 400 | invalid_request | The request body cannot be read or is not valid JSON. |
| 403 | forbidden | The credential lacks fabric:codesync.write, or has no project. |
| 502 | unconfigured | No ingestion target is configured, or no connector authority is running. |
| 401 | invalid_signature | The GitHub webhook signature is wrong or missing. |
| 404 | repo_not_bound | A push arrived for a repository never bootstrapped. |
More details
Call connector and bootstrap before the webhook can resolve a push; the three ingest routes are alternatives, not stages.
Every route but the GitHub push receiver is project-scoped REST behind one grant. The receiver is authenticated by signature rather than by credential.
One grant gates almost all of it
| Grant | Routes |
|---|---|
fabric:codesync.write | all eight credentialled routes except GET /v1/codesync/names |
The same check also requires a project-scoped credential. A token holding the grant but no project
is refused with "code sync requires a project-scoped token (re-consent with a project selected)".
GET /v1/codesync/names
The identity echo. It is the one route on this page that needs no grant, and it exists so a client can discover which organization, department, project and user its credential resolves to before it starts sending data anywhere.
curl -sS -H "Authorization: Bearer $FABRIC_API_KEY" \
https://fabric.wexa.ai/v1/codesync/names{ "org_id": "org_abc", "dept_id": "dept_abc", "project_id": "proj_abc", "user_id": "user_abc" }Where the platform can resolve them, the response also carries the human-readable names alongside the ids. The run quoted above could not, so only the ids came back — treat the name fields as present-when-available rather than guaranteed.
POST /v1/codesync/connector
Finds or provisions the codebase connector for the token's project, then triggers a connector synchronisation. Idempotent.
The body is empty. When the connector authority is unreachable the call fails loudly rather than half-succeeding:
{ "error": "data_service_unreachable",
"error_description": "the connector authority refused the connection" }POST /v1/codesync/bootstrap
Binds a repository — by URL and name — to the project's connector, and returns the binding the GitHub webhook later looks up.
curl -sS -X POST https://fabric.wexa.ai/v1/codesync/bootstrap \
-H "Authorization: Bearer $FABRIC_API_KEY" -H "Content-Type: application/json" \
-d '{"repo_url":"https://github.com/acme/widgets","repo_name":"acme/widgets","ttl_seconds":3600}'| Field | Meaning |
|---|---|
repo_url | the clone URL, matched against the webhook payload's clone_url |
repo_name | the owner/name form, matched against the payload's full_name |
ttl_seconds | how long the binding stays valid |
POST /v1/codesync/ingest-event and POST /v1/codesync/bulk-ingest
The two synchronous ingest routes. ingest-event carries one change; bulk-ingest carries a batch.
Both take a client-shaped body, forward it, and return the ingestion target's JSON verbatim with its
status code.
curl -sS -X POST https://fabric.wexa.ai/v1/codesync/ingest-event \
-H "Authorization: Bearer $FABRIC_API_KEY" -H "Content-Type: application/json" \
-d '{"connector_id":"conn_abc",
"nodes":[{"id":"f1","kind":"File","path":"src/app.ts"}],
"edges":[]}'{ "ok": true, "nodes": 3, "edges": 5 }{ "ok": true, "files": 2, "nodes": 18, "edges": 41 }Request bodies are capped; a body over the limit is refused rather than truncated.
If the ingestion target is not configured on the deployment, these routes answer 502 with
{"error":"unconfigured", ...} with a description saying the ingestion target is not set rather than pretending
to have accepted the data.
POST /v1/codesync/ingest-async and GET /v1/codesync/ingest-status/{job_id}
The asynchronous pair, which is what the editor extension uses for a full extraction: a first request queues the work and returns immediately, and a second route polls it.
The payload the queue receives carries the token's organization and project, whatever the client sent. Poll on an interval, not in a tight loop: the status route counts against the project and organization quota windows.
{ "job_id": "job_abc_0001", "status": "queued" }curl -sS -H "Authorization: Bearer $FABRIC_API_KEY" \
https://fabric.wexa.ai/v1/codesync/ingest-status/job_abc_0001{ "job_id": "job_abc_0001", "status": "completed", "nodes": 12, "edges": 30, "processed": 42 }GET /v1/codesync/bolt-credentials
Returns the connection details for reading the project's graph directly, for clients that query it rather than going through the gateway.
{ "uri": "bolt://graph.example.com:7687", "username": "proj_abc",
"password": "…", "database": "proj_abc" }The response is a secret: do not log it, and do not cache it where a second project could read it. The values above are from a stand-in target, not a real deployment.
POST /v1/codesync/webhook/github
The GitHub push receiver. It takes no bearer token — GitHub does not hold one — and is authenticated
by an HMAC-SHA256 signature over the raw request body, sent in X-Hub-Signature-256.
Four outcomes, all observed live on a gateway with the shared secret configured:
| Request | Response |
|---|---|
X-GitHub-Event: ping, correct signature | 200 {"ok":true,"pong":true} |
| any event, wrong or missing signature | 401 {"error":"invalid_signature","error_description":"GitHub webhook signature mismatch"} |
X-GitHub-Event: issues (or any non-push, non-ping event) | 200 {"ok":true,"ignored":"issues"} |
X-GitHub-Event: push for a repository never bootstrapped | 404 {"error":"repo_not_bound","error_description":"call POST /v1/codesync/bootstrap with repo_name first, …"} |
Per-route ledger
| Route | Result |
|---|---|
GET /v1/codesync/names | 200 with the four resolved ids |
POST /v1/codesync/connector | 403 without the grant, 502 with it (no connector authority running) |
POST /v1/codesync/bootstrap | 403 without the grant, 502 with it (no connector authority running) |
POST /v1/codesync/ingest-event | 403 without the grant, 200 with it — tenant pinning observed downstream |
POST /v1/codesync/bulk-ingest | 403 without the grant, 200 with it |
POST /v1/codesync/ingest-async | 200, job_id returned |
GET /v1/codesync/ingest-status/{job_id} | 200, status echoed |
GET /v1/codesync/bolt-credentials | 403 without the grant, 200 with it |
POST /v1/codesync/webhook/github | all four outcomes above, signed and unsigned |
The nodes, edges and processed counts quoted depend on what your own extraction produces.
Every tool call works the same way: How every tool call works.