# Projects

Projects (sites followed over time) and their health scorecard.

## List projects

`GET /v1/projects`

Every project of the token's team, newest first.


- Required scopes: `projects:read`
- Rate-limit cost: 1 unit(s)

### Parameters

- `cursor` (query, string, optional): Opaque cursor from `meta.next_cursor`. It is bound to the request that issued it; reusing it with other filters returns `400 pagination.invalid_cursor`.
- `limit` (query, integer, optional): Items per page, 1 to 100 (default 25).

### Responses

- `200`: OK.
- `400`: Malformed request, such as an invalid pagination cursor (`pagination.invalid_cursor`).
- `422`: Invalid query or body values (`validation_failed`), with `errors` by field.
- `401`: Missing, invalid or expired token, or a member who left the team.
- `403`: The plan does not include the API (`api.not_in_plan`) or this verb
(`api.write_not_in_plan`), or the token lacks a scope
(`token.missing_scope`).

- `404`: No such resource for the token's team. Resources of another team answer 404 as well, never 403.
- `429`: The plan rate limit is exhausted for one window.

### Request example (cURL)

```bash
curl "https://api.nessflow.com/v1/projects" \
  -H "Authorization: Bearer $NESSFLOW_TOKEN"
```

### Response example

```json
{
    "data": [
        {
            "id": "0190f5a2-7c4e-7b1a-9d3e-2f6b8c1a4e57",
            "name": "Exemple",
            "slug": "string",
            "url": "https://exemple.fr/",
            "domain": "string",
            "locked": true,
            "created_at": "2026-10-01T09:30:00Z",
            "latest_completed_crawl": {
                "id": "0190f5a2-7c4e-7b1a-9d3e-2f6b8c1a4e57",
                "finished_at": "string"
            }
        }
    ],
    "meta": {
        "limit": 100,
        "has_more": true,
        "next_cursor": "string"
    }
}
```

## Create a project

`POST /v1/projects`

Creates a project and its default campaign, under the same rules as the
product: the URL must be a public site, and the plan project limit applies.


- Required scopes: `projects:write`
- Rate-limit cost: 1 unit(s)
- Idempotency-Key accepted

### Request body

- `url` (string, required): Public site URL.
- `name` (string | null, optional): Defaults to the host name.

### Responses

- `201`: OK.
- `400`: Malformed request, such as an invalid pagination cursor (`pagination.invalid_cursor`).
- `401`: Missing, invalid or expired token, or a member who left the team.
- `403`: The plan does not include the API (`api.not_in_plan`) or this verb
(`api.write_not_in_plan`), or the token lacks a scope
(`token.missing_scope`).

- `404`: No such resource for the token's team. Resources of another team answer 404 as well, never 403.
- `409`: The request conflicts with the current state (a crawl already running, an idempotency key in use, a project not ready).
- `422`: Invalid query or body values (`validation_failed`), with `errors` by field.
- `429`: The plan rate limit is exhausted for one window.

### Request example (cURL)

```bash
curl -X POST "https://api.nessflow.com/v1/projects" \
  -H "Authorization: Bearer $NESSFLOW_TOKEN" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{"url":"https://exemple.fr/","name":"Exemple"}'
```

### Response example

```json
{
    "data": {
        "id": "0190f5a2-7c4e-7b1a-9d3e-2f6b8c1a4e57",
        "name": "Exemple",
        "slug": "string",
        "url": "https://exemple.fr/",
        "domain": "string",
        "locked": true,
        "created_at": "2026-10-01T09:30:00Z",
        "latest_completed_crawl": {
            "id": "0190f5a2-7c4e-7b1a-9d3e-2f6b8c1a4e57",
            "finished_at": "string"
        }
    }
}
```

## Get a project

`GET /v1/projects/{project}`

A project and its latest completed crawl.


- Required scopes: `projects:read`
- Rate-limit cost: 1 unit(s)

### Parameters

- `project` (path, string (uuid), required)

### Responses

- `200`: OK.
- `401`: Missing, invalid or expired token, or a member who left the team.
- `403`: The plan does not include the API (`api.not_in_plan`) or this verb
(`api.write_not_in_plan`), or the token lacks a scope
(`token.missing_scope`).

- `404`: No such resource for the token's team. Resources of another team answer 404 as well, never 403.
- `429`: The plan rate limit is exhausted for one window.

### Request example (cURL)

```bash
curl "https://api.nessflow.com/v1/projects/0190f5a2-7c4e-7b1a-9d3e-2f6b8c1a4e57" \
  -H "Authorization: Bearer $NESSFLOW_TOKEN"
```

### Response example

```json
{
    "data": {
        "id": "0190f5a2-7c4e-7b1a-9d3e-2f6b8c1a4e57",
        "name": "Exemple",
        "slug": "string",
        "url": "https://exemple.fr/",
        "domain": "string",
        "locked": true,
        "created_at": "2026-10-01T09:30:00Z",
        "latest_completed_crawl": {
            "id": "0190f5a2-7c4e-7b1a-9d3e-2f6b8c1a4e57",
            "finished_at": "string"
        }
    }
}
```

## Update a project

`PATCH /v1/projects/{project}`

Changes the name or the URL. Changing the URL clears what described the
previous site (profile, priority URLs). A read-only project answers
`403 project.locked`.


- Required scopes: `projects:write`
- Rate-limit cost: 1 unit(s)

### Parameters

- `project` (path, string (uuid), required)

### Request body

- `name` (string, optional)
- `url` (string, optional)

### Responses

- `200`: OK.
- `401`: Missing, invalid or expired token, or a member who left the team.
- `403`: The plan does not include the API (`api.not_in_plan`) or this verb
(`api.write_not_in_plan`), or the token lacks a scope
(`token.missing_scope`).

- `404`: No such resource for the token's team. Resources of another team answer 404 as well, never 403.
- `422`: Invalid query or body values (`validation_failed`), with `errors` by field.
- `429`: The plan rate limit is exhausted for one window.

### Request example (cURL)

```bash
curl -X PATCH "https://api.nessflow.com/v1/projects/0190f5a2-7c4e-7b1a-9d3e-2f6b8c1a4e57" \
  -H "Authorization: Bearer $NESSFLOW_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"name":"Exemple","url":"https://exemple.fr/"}'
```

### Response example

```json
{
    "data": {
        "id": "0190f5a2-7c4e-7b1a-9d3e-2f6b8c1a4e57",
        "name": "Exemple",
        "slug": "string",
        "url": "https://exemple.fr/",
        "domain": "string",
        "locked": true,
        "created_at": "2026-10-01T09:30:00Z",
        "latest_completed_crawl": {
            "id": "0190f5a2-7c4e-7b1a-9d3e-2f6b8c1a4e57",
            "finished_at": "string"
        }
    }
}
```

## Delete a project

`DELETE /v1/projects/{project}`

Deletes the project and everything it holds, for good. Allowed on a
read-only project.


- Required scopes: `projects:write`
- Rate-limit cost: 1 unit(s)

### Parameters

- `project` (path, string (uuid), required)

### Responses

- `204`: Deleted.
- `401`: Missing, invalid or expired token, or a member who left the team.
- `403`: The plan does not include the API (`api.not_in_plan`) or this verb
(`api.write_not_in_plan`), or the token lacks a scope
(`token.missing_scope`).

- `404`: No such resource for the token's team. Resources of another team answer 404 as well, never 403.
- `429`: The plan rate limit is exhausted for one window.

### Request example (cURL)

```bash
curl -X DELETE "https://api.nessflow.com/v1/projects/0190f5a2-7c4e-7b1a-9d3e-2f6b8c1a4e57" \
  -H "Authorization: Bearer $NESSFLOW_TOKEN"
```

## Get the health scorecard

`GET /v1/projects/{project}/scorecard`

The five pillars of the health score (technical, content and GEO,
accessibility, Search Console acquisition, log exploration), as on the
project screen. There is no global average by design: pillars of different
natures do not add up. A pillar that cannot be measured has `score: null`,
never `0`.


- Required scopes: `projects:read`
- Rate-limit cost: 5 unit(s)

### Parameters

- `project` (path, string (uuid), required)

### Responses

- `200`: OK.
- `401`: Missing, invalid or expired token, or a member who left the team.
- `403`: The plan does not include the API (`api.not_in_plan`) or this verb
(`api.write_not_in_plan`), or the token lacks a scope
(`token.missing_scope`).

- `404`: No such resource for the token's team. Resources of another team answer 404 as well, never 403.
- `429`: The plan rate limit is exhausted for one window.

### Request example (cURL)

```bash
curl "https://api.nessflow.com/v1/projects/0190f5a2-7c4e-7b1a-9d3e-2f6b8c1a4e57/scorecard" \
  -H "Authorization: Bearer $NESSFLOW_TOKEN"
```

### Response example

```json
{
    "data": {
        "project_id": "0190f5a2-7c4e-7b1a-9d3e-2f6b8c1a4e57",
        "pillars": [
            {
                "key": "technique",
                "score": 42,
                "status": "good",
                "available": true,
                "version": 42,
                "as_of": "string",
                "blocked_reason": "string",
                "axes": [
                    {}
                ]
            }
        ]
    }
}
```
