# Links API

Service links. 4 operations under /api/v1. Errors are RFC 9457 problem+json with a stable `code` field. The complete schemas are in https://nebulactrl.com/api/v1/openapi.json.

## GET /environments/{environmentId}/link-proposals

Preview the variables linking two services would propose

Writes nothing. Returns every reference the consumer (`from`) could hold to reach the provider (`to`), the recommended set flagged and each row's status on the consumer, plus warnings and whether an edge already exists. Add the chosen ones with PUT /services/{id}/variables; the edge is drawn from those references.

Authentication: session cookie or `Authorization: Bearer nct_…` token.

Parameters:
- `environmentId` (path, string, required)
- `from` (query, string): The consumer: the service that would hold the variables
- `to` (query, string): The provider: the service the variables point at

Response 200: OK (LinkPreview)
  - `existingKeys` (string[] | null, required)
  - `linked` (boolean, required)
  - `mesh` (MeshPlan, required)
  - `proposedVariables` (ProposedVariable[] | null, required)
  - `warnings` (string[] | null, required)
Errors: 401 Unauthorized; 403 Forbidden; 404 Not Found; 422 Unprocessable Entity; 500 Internal Server Error

## GET /environments/{environmentId}/links

List the links between services of an environment

Authentication: session cookie or `Authorization: Bearer nct_…` token.

Parameters:
- `environmentId` (path, string, required)

Response 200: OK (LinkListOutputBody)
  - `items` (LinkView[] | null, required)
Errors: 401 Unauthorized; 403 Forbidden; 404 Not Found; 422 Unprocessable Entity; 500 Internal Server Error

## POST /environments/{environmentId}/links

Link two services

A link is presentation plus review only. The response proposes variables for the source service; they are NOT added. Add them with PUT /services/{id}/variables after the user confirms. When the two services run on different servers the link also opens a mesh grant (cross-server service link): the response's mesh block lists the links and devices; devices need acceptMeshChanges.

Authentication: session cookie or `Authorization: Bearer nct_…` token.

Parameters:
- `environmentId` (path, string, required)

Request body (application/json, required): CreateLinkInputBody
  - `acceptMeshChanges` (boolean): Confirm the tailnet devices a link between services on different servers adds; without it the request fails with mesh_confirmation_required when devices would be added
  - `fromServiceId` (string, required)
  - `toServiceId` (string, required)

Response 201: Created (LinkResult)
  - `link` (LinkView, required)
  - `mesh` (MeshPlan, required)
  - `proposedVariables` (ProposedVariable[] | null, required)
  - `warnings` (string[] | null, required)
Errors: 401 Unauthorized; 403 Forbidden; 404 Not Found; 409 Conflict; 422 Unprocessable Entity; 500 Internal Server Error

## DELETE /links/{linkId}

Remove a link

Authentication: session cookie or `Authorization: Bearer nct_…` token.

Parameters:
- `linkId` (path, string, required): UUID of a manual link; derived reference links (ref:<from>:<to>) cannot be deleted

Response 204: No Content
Errors: 401 Unauthorized; 403 Forbidden; 404 Not Found; 422 Unprocessable Entity; 500 Internal Server Error
