# Projects

URL: https://submitator.com/docs/api/projects

> A project is one of your client's products.

A project is one of your client's products. Create it from a URL and we fill in what the site tells us; then add a logo, 1 to 5 screenshots and a contact email.

A draft costs nothing and you can delete it. Once launched, a project stays: it cannot be deleted, and its `contact_email` can no longer change.

## List projects

`GET https://api.submitator.com/v1/projects`

Returns your projects in the key's mode, newest first. Filter by `status`, find one by your own `external_id`, or search names and URLs with `q`.

| Parameter | In | Required | What it is |
| --- | --- | --- | --- |
| `status` | query | No | Only projects in this status. |
| `external_id` | query | No | Only the project with this `external_id`. Matches the whole value, case-sensitive. |
| `q` | query | No | Only projects whose name or URL contains this text, ignoring case. 2 to 100 characters. |
| `cursor` | query | No | `next_cursor` from the previous page of the same list, as you received it. Leave it out for the first page. |
| `limit` | query | No | How many items to return, 1 to 100. Defaults to 20. |

Response 200:

```json
{
  "object": "list",
  "data": [
    {
      "object": "project",
      "id": "prj_1Nc7Vd3Kq8Zr5Tx2Wm9LbH",
      "livemode": false,
      "status": "in_progress",
      "hold_reason": null,
      "action": null,
      "url": "https://quotafy.test",
      "name": "Quotafy",
      "maker_name": "Tom Okafor",
      "contact_email": "sales@quotafy.test",
      "tagline": "Quotes your clients sign in one click",
      "short_description": "Quotafy turns a price list into quotes that clients accept and sign online, with reminders and a deposit link.",
      "long_description": null,
      "category": "saas",
      "pricing_model": "paid",
      "price_amount_cents": 2900,
      "price_period": "monthly",
      "launch_date": "2026-08-20",
      "twitter_url": "https://x.com/quotafy",
      "competitors": [
        {
          "name": "Bidsheet",
          "url": "https://bidsheet.test"
        }
      ],
      "tags": [
        "quotes",
        "proposals",
        "e-signature"
      ],
      "audience": {
        "b2b": true,
        "ai": false,
        "directory": false
      },
      "promo_code": "QUOTE30",
      "promo_description": "30% off the first 3 months",
      "logo": {
        "object": "image",
        "id": "img_1Qa8Kx3Wm7Lt2Vb9Rz5NcE",
        "status": "ready",
        "url": "https://feed.example.net/a/Qf2Lm8Wx.k5Tp3",
        "error": null
      },
      "screenshots": [
        {
          "object": "image",
          "id": "img_4Qb2Kw9Lx5Mt8Vb3Rz7NdG",
          "status": "ready",
          "url": "https://feed.example.net/a/Qg9Nt4Vb.z7Kr2",
          "error": null
        }
      ],
      "autofill": {
        "status": "complete",
        "name_source": "input",
        "filled": [],
        "image_candidates": []
      },
      "readiness": {
        "ready": true,
        "missing": []
      },
      "domain_rating": 12,
      "badge": {
        "status": "not_checked",
        "checked_at": null
      },
      "auto_replace": true,
      "excluded_directories": [
        "dir_3Vb8Qm2Xk6Lt9Wr4Nc1ZpG"
      ],
      "allowance": {
        "total": 100,
        "used": 72,
        "reserved": 20,
        "available": 8
      },
      "progress": {
        "total": 92,
        "pending": 0,
        "in_progress": 20,
        "action_required": 0,
        "submitted": 33,
        "live": 39,
        "not_accepted": 0,
        "cancelled": 0
      },
      "external_id": "crm_8907",
      "metadata": {},
      "created_at": "2026-10-05T09:40:18Z",
      "updated_at": "2026-10-08T13:31:52Z",
      "launched_at": "2026-10-05T10:12:07Z"
    },
    {
      "object": "project",
      "id": "prj_3Ac9Kx4Wq7Lt2Vb8Rz5NmD",
      "livemode": false,
      "status": "completed",
      "hold_reason": null,
      "action": null,
      "url": "https://acme.test",
      "name": "Acme CRM",
      "maker_name": "Lena Park",
      "contact_email": "hello@acme.test",
      "tagline": "The CRM for teams of five",
      "short_description": "Acme CRM keeps contacts, deals and follow-ups in one shared list, with a weekly digest for the whole team.",
      "long_description": null,
      "category": "saas",
      "pricing_model": "freemium",
      "price_amount_cents": 900,
      "price_period": "monthly",
      "launch_date": null,
      "twitter_url": null,
      "competitors": [],
      "tags": [
        "crm",
        "sales"
      ],
      "audience": {
        "b2b": true,
        "ai": false,
        "directory": false
      },
      "promo_code": null,
      "promo_description": null,
      "logo": {
        "object": "image",
        "id": "img_3Ad7Kx2Wq8Lt4Vb9Rz1NmH",
        "status": "ready",
        "url": "https://feed.example.net/a/Ac6Rk1Wn.p4Lx8",
        "error": null
      },
      "screenshots": [
        {
          "object": "image",
          "id": "img_0Ae5Kw1Lq9Xt3Vb7Rz2McJ",
          "status": "ready",
          "url": "https://feed.example.net/a/Ad3Mq7Zt.v9Kc1",
          "error": null
        }
      ],
      "autofill": {
        "status": "complete",
        "name_source": "site",
        "filled": [
          "name",
          "tagline"
        ],
        "image_candidates": []
      },
      "readiness": {
        "ready": true,
        "missing": []
      },
      "domain_rating": 51,
      "badge": {
        "status": "installed",
        "checked_at": "2026-10-03T15:08:30Z"
      },
      "auto_replace": false,
      "excluded_directories": [],
      "allowance": {
        "total": 100,
        "used": 85,
        "reserved": 0,
        "available": 15
      },
      "progress": {
        "total": 88,
        "pending": 0,
        "in_progress": 0,
        "action_required": 0,
        "submitted": 3,
        "live": 82,
        "not_accepted": 2,
        "cancelled": 1
      },
      "external_id": "crm_8650",
      "metadata": {},
      "created_at": "2026-10-03T15:01:44Z",
      "updated_at": "2026-10-07T18:22:47Z",
      "launched_at": "2026-10-03T15:12:09Z"
    }
  ],
  "has_more": true,
  "next_cursor": "cur_Pq7Lx2Wm9Kt4Vb8R"
}
```

Typical errors: [`validation_failed`](https://submitator.com/docs/errors#validation_failed), [`invalid_cursor`](https://submitator.com/docs/errors#invalid_cursor).

## Create a project

`POST https://api.submitator.com/v1/projects`

Creates a draft for one of your client's products. Only `url` is required: we read the site and fill in the name, tagline and short description you leave out. Images given as `logo_url` and `screenshot_urls` import in the background, so they show `importing` first. A draft costs nothing.

| Parameter | In | Required | What it is |
| --- | --- | --- | --- |
| `Idempotency-Key` | header | No | A unique string, such as a UUID, that makes a retry safe. A success is kept for 24 hours: a repeat with the same key and body returns it again with `Idempotent-Replayed: true`, and the same key with another body returns 422 `idempotency_key_reused`. A repeat while the first request runs returns 409 `idempotency_key_in_use`. An error changes nothing, so a repeat after one runs again. |

| Body field | Type | Required | What it is |
| --- | --- | --- | --- |
| `url` | string | Yes | Your client's site, `http` or `https`, up to 2,048 characters. Unique among your projects in the mode. Live mode accepts public addresses only: no IP addresses and no `.test`, `.example`, `.invalid` or `.localhost` hosts. |
| `name` | string | No | The product's name as directories show it, 2 to 100 characters. Until you set one, it comes from the domain, and once autofill finishes, from the site's title. |
| `maker_name` | string or null | No | The person directories list as the maker, up to 100 characters. |
| `contact_email` | string or null | No | Required to launch. Some directories register the listing to this address and send their emails here. Unique among your projects in the mode, and fixed once the project launches. |
| `tagline` | string or null | No | One line about the product, up to 100 characters. |
| `short_description` | string or null | No | A short description, up to 500 characters. Most directories show this one. |
| `long_description` | string or null | No | A longer description, up to 2,000 characters, for directories that take one. |
| `category` | string or null | No | The product's category. We map it to each directory's own list of categories. |
| `pricing_model` | string or null | No | How the product is sold. |
| `price_amount_cents` | integer or null | No | The headline price in US cents, such as `1200` for $12. Leave it `null` for a free product. |
| `price_period` | string or null | No | What `price_amount_cents` pays for. |
| `launch_date` | string or null | No | The day the product went public, `YYYY-MM-DD`, for directories that ask for it. Not the day the project launched here: that is `launched_at`. |
| `twitter_url` | string or null | No | The product's profile on X, as an `https://x.com/` or `https://twitter.com/` URL. |
| `competitors` | array | No | Up to 20 products this one is an alternative to. Some directories list a product only next to its competitors. Sending a list replaces the whole list. |
| `tags` | array | No | Up to 10 keywords of up to 40 characters each. Sending a list replaces the whole list. |
| `audience` | object | No | Marks that open the directories which accept only some kinds of products. |
| `promo_code` | string or null | No | A discount code for the product, up to 50 characters. Some directories list only products with one. |
| `promo_description` | string or null | No | What `promo_code` gives, up to 500 characters, such as 30% off the first 3 months. |
| `logo_url` | string | No | A public URL of the logo, imported in the background: PNG, JPEG, GIF or WebP, up to 5 MB. `logo.status` shows `importing` until it is done. |
| `screenshot_urls` | array | No | 1 to 5 public URLs of screenshots, imported in the background in this order. Directories that take a single screenshot use the first. |
| `external_id` | string or null | No | Your own id for the project, up to 100 characters, such as the client's id in your CRM. Unique among your projects in the mode; find a project by it with `GET /projects?external_id=`. |
| `metadata` | object | No | Your own key-value data. We store it and return it, and use it for nothing else. |
| `excluded_directories` | array | No | Directories never to use for this project: `auto` launches, `auto` adds and auto-replace skip them. A launch with `exclude` adds its list here. Sending a list replaces the whole list. |
| `auto_replace` | boolean | No | New projects start with `true`. When `true`, we add fitting directories from the free allowance for you: after a listing ends `not_accepted` or `cancelled`, after the badge passes verification, and when a new directory joins the catalog. A launch with `include` turns it off unless the launch sends `auto_replace: true`. |

Request:

```json
{
  "url": "https://ledgerly.test",
  "contact_email": "founder@ledgerly.test",
  "external_id": "crm_8812",
  "logo_url": "https://ledgerly.test/press/logo.png",
  "screenshot_urls": [
    "https://ledgerly.test/press/dashboard.png"
  ]
}
```

Response 201:

```json
{
  "object": "project",
  "id": "prj_4QzX1m9Lr2Vb7Nc8Tk3HwP",
  "livemode": false,
  "status": "draft",
  "hold_reason": null,
  "action": null,
  "url": "https://ledgerly.test",
  "name": "Ledgerly",
  "maker_name": null,
  "contact_email": "founder@ledgerly.test",
  "tagline": null,
  "short_description": null,
  "long_description": null,
  "category": null,
  "pricing_model": null,
  "price_amount_cents": null,
  "price_period": null,
  "launch_date": null,
  "twitter_url": null,
  "competitors": [],
  "tags": [],
  "audience": {
    "b2b": false,
    "ai": false,
    "directory": false
  },
  "promo_code": null,
  "promo_description": null,
  "logo": {
    "object": "image",
    "id": null,
    "status": "importing",
    "url": null,
    "error": null
  },
  "screenshots": [
    {
      "object": "image",
      "id": null,
      "status": "importing",
      "url": null,
      "error": null
    }
  ],
  "autofill": {
    "status": "running",
    "name_source": "domain",
    "filled": [],
    "image_candidates": []
  },
  "readiness": {
    "ready": false,
    "missing": [
      "logo",
      "screenshots"
    ]
  },
  "domain_rating": null,
  "badge": {
    "status": "not_checked",
    "checked_at": null
  },
  "auto_replace": true,
  "excluded_directories": [],
  "allowance": null,
  "progress": null,
  "external_id": "crm_8812",
  "metadata": {},
  "created_at": "2026-10-03T13:58:12Z",
  "updated_at": "2026-10-03T13:58:12Z",
  "launched_at": null
}
```

Typical errors: [`validation_failed`](https://submitator.com/docs/errors#validation_failed), [`project_exists`](https://submitator.com/docs/errors#project_exists), [`quota_exceeded`](https://submitator.com/docs/errors#quota_exceeded), [`account_suspended`](https://submitator.com/docs/errors#account_suspended).

## Read a project

`GET https://api.submitator.com/v1/projects/{project_id}`

Returns the project with its status, readiness, allowance and listing counts. Poll it, or follow `project.*` events, to learn about changes.

| Parameter | In | Required | What it is |
| --- | --- | --- | --- |
| `project_id` | path | Yes | The project's id. |

Response 200:

```json
{
  "object": "project",
  "id": "prj_4QzX1m9Lr2Vb7Nc8Tk3HwP",
  "livemode": false,
  "status": "action_required",
  "hold_reason": null,
  "action": {
    "code": "install_badge",
    "waiting_on": "client",
    "client_message": "Please add the directory badges block to https://ledgerly.test. 9 directories list Ledgerly once their badges show on the site."
  },
  "url": "https://ledgerly.test",
  "name": "Ledgerly",
  "maker_name": "Maya Chen",
  "contact_email": "founder@ledgerly.test",
  "tagline": "Double-entry bookkeeping for solo founders",
  "short_description": "Ledgerly keeps the books for one-person companies: bank sync, invoices and a year-end pack for your accountant.",
  "long_description": null,
  "category": "saas",
  "pricing_model": "freemium",
  "price_amount_cents": 1200,
  "price_period": "monthly",
  "launch_date": null,
  "twitter_url": null,
  "competitors": [],
  "tags": [
    "accounting",
    "bookkeeping",
    "invoicing"
  ],
  "audience": {
    "b2b": false,
    "ai": false,
    "directory": false
  },
  "promo_code": null,
  "promo_description": null,
  "logo": {
    "object": "image",
    "id": "img_5Mx2Kq8Wt3Lr9Vb7Nz1HdQ",
    "status": "ready",
    "url": "https://feed.example.net/a/Lq7Wx2Km.r9Tz4",
    "error": null
  },
  "screenshots": [
    {
      "object": "image",
      "id": "img_2Kr9Lx3Wq7Mt5Vb8Nz1HcF",
      "status": "ready",
      "url": "https://feed.example.net/a/Sv3Nk8Qp.m2Wx7",
      "error": null
    }
  ],
  "autofill": {
    "status": "complete",
    "name_source": "site",
    "filled": [
      "name",
      "tagline",
      "short_description"
    ],
    "image_candidates": [
      {
        "url": "https://ledgerly.test/apple-touch-icon.png",
        "suggested_as": "logo",
        "width": 180,
        "height": 180
      },
      {
        "url": "https://ledgerly.test/og-image.png",
        "suggested_as": "screenshot",
        "width": 1200,
        "height": 630
      }
    ]
  },
  "readiness": {
    "ready": true,
    "missing": []
  },
  "domain_rating": 34,
  "badge": {
    "status": "not_found",
    "checked_at": "2026-10-08T09:12:44Z"
  },
  "auto_replace": true,
  "excluded_directories": [],
  "allowance": {
    "total": 100,
    "used": 78,
    "reserved": 14,
    "available": 8
  },
  "progress": {
    "total": 96,
    "pending": 0,
    "in_progress": 5,
    "action_required": 9,
    "submitted": 8,
    "live": 70,
    "not_accepted": 3,
    "cancelled": 1
  },
  "external_id": "crm_8812",
  "metadata": {
    "account_owner": "Priya",
    "crm_stage": "onboarding"
  },
  "created_at": "2026-10-03T13:58:12Z",
  "updated_at": "2026-10-08T14:17:01Z",
  "launched_at": "2026-10-03T14:05:40Z"
}
```

Typical errors: [`not_found`](https://submitator.com/docs/errors#not_found), [`rate_limited`](https://submitator.com/docs/errors#rate_limited).

## Update a project

`PATCH https://api.submitator.com/v1/projects/{project_id}`

Changes the fields you send and leaves the others as they are. Send `null` or an empty string to clear a field. After launch `contact_email` can no longer change; other edits apply to listings that have not reached their directory yet.

| Parameter | In | Required | What it is |
| --- | --- | --- | --- |
| `project_id` | path | Yes | The project's id. |

| Body field | Type | Required | What it is |
| --- | --- | --- | --- |
| `url` | string | No | Your client's site, `http` or `https`, up to 2,048 characters. Unique among your projects in the mode. Live mode accepts public addresses only: no IP addresses and no `.test`, `.example`, `.invalid` or `.localhost` hosts. |
| `name` | string | No | The product's name as directories show it, 2 to 100 characters. Until you set one, it comes from the domain, and once autofill finishes, from the site's title. |
| `maker_name` | string or null | No | The person directories list as the maker, up to 100 characters. |
| `contact_email` | string or null | No | Required to launch. Some directories register the listing to this address and send their emails here. Unique among your projects in the mode, and fixed once the project launches. |
| `tagline` | string or null | No | One line about the product, up to 100 characters. |
| `short_description` | string or null | No | A short description, up to 500 characters. Most directories show this one. |
| `long_description` | string or null | No | A longer description, up to 2,000 characters, for directories that take one. |
| `category` | string or null | No | The product's category. We map it to each directory's own list of categories. |
| `pricing_model` | string or null | No | How the product is sold. |
| `price_amount_cents` | integer or null | No | The headline price in US cents, such as `1200` for $12. Leave it `null` for a free product. |
| `price_period` | string or null | No | What `price_amount_cents` pays for. |
| `launch_date` | string or null | No | The day the product went public, `YYYY-MM-DD`, for directories that ask for it. Not the day the project launched here: that is `launched_at`. |
| `twitter_url` | string or null | No | The product's profile on X, as an `https://x.com/` or `https://twitter.com/` URL. |
| `competitors` | array | No | Up to 20 products this one is an alternative to. Some directories list a product only next to its competitors. Sending a list replaces the whole list. |
| `tags` | array | No | Up to 10 keywords of up to 40 characters each. Sending a list replaces the whole list. |
| `audience` | object | No | Marks that open the directories which accept only some kinds of products. |
| `promo_code` | string or null | No | A discount code for the product, up to 50 characters. Some directories list only products with one. |
| `promo_description` | string or null | No | What `promo_code` gives, up to 500 characters, such as 30% off the first 3 months. |
| `external_id` | string or null | No | Your own id for the project, up to 100 characters, such as the client's id in your CRM. Unique among your projects in the mode; find a project by it with `GET /projects?external_id=`. |
| `metadata` | object | No | Your own key-value data. We store it and return it, and use it for nothing else. |
| `excluded_directories` | array | No | Directories never to use for this project: `auto` launches, `auto` adds and auto-replace skip them. A launch with `exclude` adds its list here. Sending a list replaces the whole list. |
| `auto_replace` | boolean | No | New projects start with `true`. When `true`, we add fitting directories from the free allowance for you: after a listing ends `not_accepted` or `cancelled`, after the badge passes verification, and when a new directory joins the catalog. A launch with `include` turns it off unless the launch sends `auto_replace: true`. |

Request:

```json
{
  "maker_name": "Maya Chen",
  "category": "saas",
  "pricing_model": "freemium",
  "price_amount_cents": 1200,
  "price_period": "monthly",
  "tags": [
    "accounting",
    "bookkeeping",
    "invoicing"
  ],
  "metadata": {
    "account_owner": "Priya",
    "crm_stage": "onboarding"
  }
}
```

Response 200:

```json
{
  "object": "project",
  "id": "prj_4QzX1m9Lr2Vb7Nc8Tk3HwP",
  "livemode": false,
  "status": "draft",
  "hold_reason": null,
  "action": null,
  "url": "https://ledgerly.test",
  "name": "Ledgerly",
  "maker_name": "Maya Chen",
  "contact_email": "founder@ledgerly.test",
  "tagline": "Double-entry bookkeeping for solo founders",
  "short_description": "Ledgerly keeps the books for one-person companies: bank sync, invoices and a year-end pack for your accountant.",
  "long_description": null,
  "category": "saas",
  "pricing_model": "freemium",
  "price_amount_cents": 1200,
  "price_period": "monthly",
  "launch_date": null,
  "twitter_url": null,
  "competitors": [],
  "tags": [
    "accounting",
    "bookkeeping",
    "invoicing"
  ],
  "audience": {
    "b2b": false,
    "ai": false,
    "directory": false
  },
  "promo_code": null,
  "promo_description": null,
  "logo": {
    "object": "image",
    "id": "img_5Mx2Kq8Wt3Lr9Vb7Nz1HdQ",
    "status": "ready",
    "url": "https://feed.example.net/a/Lq7Wx2Km.r9Tz4",
    "error": null
  },
  "screenshots": [
    {
      "object": "image",
      "id": "img_2Kr9Lx3Wq7Mt5Vb8Nz1HcF",
      "status": "ready",
      "url": "https://feed.example.net/a/Sv3Nk8Qp.m2Wx7",
      "error": null
    }
  ],
  "autofill": {
    "status": "complete",
    "name_source": "site",
    "filled": [
      "name",
      "tagline",
      "short_description"
    ],
    "image_candidates": [
      {
        "url": "https://ledgerly.test/apple-touch-icon.png",
        "suggested_as": "logo",
        "width": 180,
        "height": 180
      },
      {
        "url": "https://ledgerly.test/og-image.png",
        "suggested_as": "screenshot",
        "width": 1200,
        "height": 630
      }
    ]
  },
  "readiness": {
    "ready": true,
    "missing": []
  },
  "domain_rating": 34,
  "badge": {
    "status": "not_checked",
    "checked_at": null
  },
  "auto_replace": true,
  "excluded_directories": [],
  "allowance": null,
  "progress": null,
  "external_id": "crm_8812",
  "metadata": {
    "account_owner": "Priya",
    "crm_stage": "onboarding"
  },
  "created_at": "2026-10-03T13:58:12Z",
  "updated_at": "2026-10-03T14:02:31Z",
  "launched_at": null
}
```

Typical errors: [`validation_failed`](https://submitator.com/docs/errors#validation_failed), [`project_exists`](https://submitator.com/docs/errors#project_exists), [`not_found`](https://submitator.com/docs/errors#not_found).

## Delete a draft

`DELETE https://api.submitator.com/v1/projects/{project_id}`

Deletes a project that has not launched, with its images. A launched project cannot be deleted: the call returns 409 `already_launched` and changes nothing.

| Parameter | In | Required | What it is |
| --- | --- | --- | --- |
| `project_id` | path | Yes | The project's id. |

Response 200:

```json
{
  "object": "project",
  "id": "prj_5Ob2Kx8Wq3Lt7Vb9Rz1NmE",
  "livemode": false,
  "deleted": true
}
```

Typical errors: [`already_launched`](https://submitator.com/docs/errors#already_launched), [`not_found`](https://submitator.com/docs/errors#not_found).

## Set the logo

`PUT https://api.submitator.com/v1/projects/{project_id}/logo`

Uploads the logo as `multipart/form-data` with a `file` field, or imports it from `url` in a JSON body. PNG, JPEG, GIF or WebP up to 5 MB; it replaces the current logo. Imports from a URL count toward a limit of 60 per hour.

| Parameter | In | Required | What it is |
| --- | --- | --- | --- |
| `project_id` | path | Yes | The project's id. |

| Body field | Type | Required | What it is |
| --- | --- | --- | --- |
| `url` | string | Yes | A public URL that returns a PNG, JPEG, GIF or WebP image of up to 5 MB. You can import 60 images per hour. |

Request:

```json
{
  "url": "https://ledgerly.test/press/logo.png"
}
```

Response 200:

```json
{
  "object": "project",
  "id": "prj_4QzX1m9Lr2Vb7Nc8Tk3HwP",
  "livemode": false,
  "status": "draft",
  "hold_reason": null,
  "action": null,
  "url": "https://ledgerly.test",
  "name": "Ledgerly",
  "maker_name": null,
  "contact_email": "founder@ledgerly.test",
  "tagline": "Double-entry bookkeeping for solo founders",
  "short_description": "Ledgerly keeps the books for one-person companies: bank sync, invoices and a year-end pack for your accountant.",
  "long_description": null,
  "category": null,
  "pricing_model": null,
  "price_amount_cents": null,
  "price_period": null,
  "launch_date": null,
  "twitter_url": null,
  "competitors": [],
  "tags": [],
  "audience": {
    "b2b": false,
    "ai": false,
    "directory": false
  },
  "promo_code": null,
  "promo_description": null,
  "logo": {
    "object": "image",
    "id": "img_5Mx2Kq8Wt3Lr9Vb7Nz1HdQ",
    "status": "ready",
    "url": "https://feed.example.net/a/Lq7Wx2Km.r9Tz4",
    "error": null
  },
  "screenshots": [
    {
      "object": "image",
      "id": "img_2Kr9Lx3Wq7Mt5Vb8Nz1HcF",
      "status": "ready",
      "url": "https://feed.example.net/a/Sv3Nk8Qp.m2Wx7",
      "error": null
    }
  ],
  "autofill": {
    "status": "complete",
    "name_source": "site",
    "filled": [
      "name",
      "tagline",
      "short_description"
    ],
    "image_candidates": [
      {
        "url": "https://ledgerly.test/apple-touch-icon.png",
        "suggested_as": "logo",
        "width": 180,
        "height": 180
      },
      {
        "url": "https://ledgerly.test/og-image.png",
        "suggested_as": "screenshot",
        "width": 1200,
        "height": 630
      }
    ]
  },
  "readiness": {
    "ready": true,
    "missing": []
  },
  "domain_rating": 34,
  "badge": {
    "status": "not_checked",
    "checked_at": null
  },
  "auto_replace": true,
  "excluded_directories": [],
  "allowance": null,
  "progress": null,
  "external_id": "crm_8812",
  "metadata": {},
  "created_at": "2026-10-03T13:58:12Z",
  "updated_at": "2026-10-03T14:01:20Z",
  "launched_at": null
}
```

Typical errors: [`validation_failed`](https://submitator.com/docs/errors#validation_failed), [`image_unreachable`](https://submitator.com/docs/errors#image_unreachable), [`unsupported_media_type`](https://submitator.com/docs/errors#unsupported_media_type), [`rate_limited`](https://submitator.com/docs/errors#rate_limited).

## Remove the logo

`DELETE https://api.submitator.com/v1/projects/{project_id}/logo`

Removes the logo from a draft. A launched project must keep a logo: removing it returns 422 `validation_failed`, so replace it with `PUT` instead.

| Parameter | In | Required | What it is |
| --- | --- | --- | --- |
| `project_id` | path | Yes | The project's id. |

Response 200:

```json
{
  "object": "project",
  "id": "prj_4QzX1m9Lr2Vb7Nc8Tk3HwP",
  "livemode": false,
  "status": "draft",
  "hold_reason": null,
  "action": null,
  "url": "https://ledgerly.test",
  "name": "Ledgerly",
  "maker_name": null,
  "contact_email": "founder@ledgerly.test",
  "tagline": "Double-entry bookkeeping for solo founders",
  "short_description": "Ledgerly keeps the books for one-person companies: bank sync, invoices and a year-end pack for your accountant.",
  "long_description": null,
  "category": null,
  "pricing_model": null,
  "price_amount_cents": null,
  "price_period": null,
  "launch_date": null,
  "twitter_url": null,
  "competitors": [],
  "tags": [],
  "audience": {
    "b2b": false,
    "ai": false,
    "directory": false
  },
  "promo_code": null,
  "promo_description": null,
  "logo": null,
  "screenshots": [
    {
      "object": "image",
      "id": "img_2Kr9Lx3Wq7Mt5Vb8Nz1HcF",
      "status": "ready",
      "url": "https://feed.example.net/a/Sv3Nk8Qp.m2Wx7",
      "error": null
    }
  ],
  "autofill": {
    "status": "complete",
    "name_source": "site",
    "filled": [
      "name",
      "tagline",
      "short_description"
    ],
    "image_candidates": [
      {
        "url": "https://ledgerly.test/apple-touch-icon.png",
        "suggested_as": "logo",
        "width": 180,
        "height": 180
      },
      {
        "url": "https://ledgerly.test/og-image.png",
        "suggested_as": "screenshot",
        "width": 1200,
        "height": 630
      }
    ]
  },
  "readiness": {
    "ready": false,
    "missing": [
      "logo"
    ]
  },
  "domain_rating": 34,
  "badge": {
    "status": "not_checked",
    "checked_at": null
  },
  "auto_replace": true,
  "excluded_directories": [],
  "allowance": null,
  "progress": null,
  "external_id": "crm_8812",
  "metadata": {},
  "created_at": "2026-10-03T13:58:12Z",
  "updated_at": "2026-10-03T14:00:55Z",
  "launched_at": null
}
```

Typical errors: [`validation_failed`](https://submitator.com/docs/errors#validation_failed), [`not_found`](https://submitator.com/docs/errors#not_found).

## Add a screenshot

`POST https://api.submitator.com/v1/projects/{project_id}/screenshots`

Adds one screenshot, uploaded as `file` or imported from `url`. A project holds 1 to 5 screenshots, and directories that take a single screenshot use the first one. The new screenshot is the last item of `screenshots` in the response.

| Parameter | In | Required | What it is |
| --- | --- | --- | --- |
| `project_id` | path | Yes | The project's id. |

| Body field | Type | Required | What it is |
| --- | --- | --- | --- |
| `url` | string | Yes | A public URL that returns a PNG, JPEG, GIF or WebP image of up to 5 MB. You can import 60 images per hour. |

Request:

```json
{
  "url": "https://ledgerly.test/press/reports-page.png"
}
```

Response 200:

```json
{
  "object": "project",
  "id": "prj_4QzX1m9Lr2Vb7Nc8Tk3HwP",
  "livemode": false,
  "status": "draft",
  "hold_reason": null,
  "action": null,
  "url": "https://ledgerly.test",
  "name": "Ledgerly",
  "maker_name": "Maya Chen",
  "contact_email": "founder@ledgerly.test",
  "tagline": "Double-entry bookkeeping for solo founders",
  "short_description": "Ledgerly keeps the books for one-person companies: bank sync, invoices and a year-end pack for your accountant.",
  "long_description": null,
  "category": "saas",
  "pricing_model": "freemium",
  "price_amount_cents": 1200,
  "price_period": "monthly",
  "launch_date": null,
  "twitter_url": null,
  "competitors": [],
  "tags": [
    "accounting",
    "bookkeeping",
    "invoicing"
  ],
  "audience": {
    "b2b": false,
    "ai": false,
    "directory": false
  },
  "promo_code": null,
  "promo_description": null,
  "logo": {
    "object": "image",
    "id": "img_5Mx2Kq8Wt3Lr9Vb7Nz1HdQ",
    "status": "ready",
    "url": "https://feed.example.net/a/Lq7Wx2Km.r9Tz4",
    "error": null
  },
  "screenshots": [
    {
      "object": "image",
      "id": "img_2Kr9Lx3Wq7Mt5Vb8Nz1HcF",
      "status": "ready",
      "url": "https://feed.example.net/a/Sv3Nk8Qp.m2Wx7",
      "error": null
    },
    {
      "object": "image",
      "id": "img_6Wn3Kq8Lx2Rt7Vb9Mz4HcB",
      "status": "ready",
      "url": "https://feed.example.net/a/Tz5Kq1Wm.n8Lr3",
      "error": null
    }
  ],
  "autofill": {
    "status": "complete",
    "name_source": "site",
    "filled": [
      "name",
      "tagline",
      "short_description"
    ],
    "image_candidates": [
      {
        "url": "https://ledgerly.test/apple-touch-icon.png",
        "suggested_as": "logo",
        "width": 180,
        "height": 180
      },
      {
        "url": "https://ledgerly.test/og-image.png",
        "suggested_as": "screenshot",
        "width": 1200,
        "height": 630
      }
    ]
  },
  "readiness": {
    "ready": true,
    "missing": []
  },
  "domain_rating": 34,
  "badge": {
    "status": "not_checked",
    "checked_at": null
  },
  "auto_replace": true,
  "excluded_directories": [],
  "allowance": null,
  "progress": null,
  "external_id": "crm_8812",
  "metadata": {
    "account_owner": "Priya",
    "crm_stage": "onboarding"
  },
  "created_at": "2026-10-03T13:58:12Z",
  "updated_at": "2026-10-03T14:03:05Z",
  "launched_at": null
}
```

Typical errors: [`validation_failed`](https://submitator.com/docs/errors#validation_failed), [`image_unreachable`](https://submitator.com/docs/errors#image_unreachable), [`unsupported_media_type`](https://submitator.com/docs/errors#unsupported_media_type).

## Remove a screenshot

`DELETE https://api.submitator.com/v1/projects/{project_id}/screenshots/{image_id}`

Removes one screenshot. A launched project keeps at least 1, so removing its last screenshot returns 422 `validation_failed`.

| Parameter | In | Required | What it is |
| --- | --- | --- | --- |
| `project_id` | path | Yes | The project's id. |
| `image_id` | path | Yes | The screenshot's id, from `screenshots[].id` on the project. |

Response 200:

```json
{
  "object": "project",
  "id": "prj_4QzX1m9Lr2Vb7Nc8Tk3HwP",
  "livemode": false,
  "status": "draft",
  "hold_reason": null,
  "action": null,
  "url": "https://ledgerly.test",
  "name": "Ledgerly",
  "maker_name": "Maya Chen",
  "contact_email": "founder@ledgerly.test",
  "tagline": "Double-entry bookkeeping for solo founders",
  "short_description": "Ledgerly keeps the books for one-person companies: bank sync, invoices and a year-end pack for your accountant.",
  "long_description": null,
  "category": "saas",
  "pricing_model": "freemium",
  "price_amount_cents": 1200,
  "price_period": "monthly",
  "launch_date": null,
  "twitter_url": null,
  "competitors": [],
  "tags": [
    "accounting",
    "bookkeeping",
    "invoicing"
  ],
  "audience": {
    "b2b": false,
    "ai": false,
    "directory": false
  },
  "promo_code": null,
  "promo_description": null,
  "logo": {
    "object": "image",
    "id": "img_5Mx2Kq8Wt3Lr9Vb7Nz1HdQ",
    "status": "ready",
    "url": "https://feed.example.net/a/Lq7Wx2Km.r9Tz4",
    "error": null
  },
  "screenshots": [
    {
      "object": "image",
      "id": "img_2Kr9Lx3Wq7Mt5Vb8Nz1HcF",
      "status": "ready",
      "url": "https://feed.example.net/a/Sv3Nk8Qp.m2Wx7",
      "error": null
    }
  ],
  "autofill": {
    "status": "complete",
    "name_source": "site",
    "filled": [
      "name",
      "tagline",
      "short_description"
    ],
    "image_candidates": [
      {
        "url": "https://ledgerly.test/apple-touch-icon.png",
        "suggested_as": "logo",
        "width": 180,
        "height": 180
      },
      {
        "url": "https://ledgerly.test/og-image.png",
        "suggested_as": "screenshot",
        "width": 1200,
        "height": 630
      }
    ]
  },
  "readiness": {
    "ready": true,
    "missing": []
  },
  "domain_rating": 34,
  "badge": {
    "status": "not_checked",
    "checked_at": null
  },
  "auto_replace": true,
  "excluded_directories": [],
  "allowance": null,
  "progress": null,
  "external_id": "crm_8812",
  "metadata": {
    "account_owner": "Priya",
    "crm_stage": "onboarding"
  },
  "created_at": "2026-10-03T13:58:12Z",
  "updated_at": "2026-10-03T14:03:40Z",
  "launched_at": null
}
```

Typical errors: [`validation_failed`](https://submitator.com/docs/errors#validation_failed), [`not_found`](https://submitator.com/docs/errors#not_found).
