# API reference

Base URL: `https://api.heremyapp.com/v1`. Requests and responses are JSON, except site uploads (tar or tar.gz) and installer
uploads (raw bytes). Authenticate with `Authorization: Bearer hma_...`.

## Endpoints

| Method | Path | Purpose | Token |
|---|---|---|---|
| GET | `/me` | Account and token scope | any |
| POST | `/spaces` | Create a space `{slug, name?}` | account |
| GET | `/spaces` | List spaces | any |
| GET | `/spaces/{slug}` | Space details, including `active_deployment_id` | any |
| POST | `/spaces/{slug}/deployments` | Deploy a tar or tar.gz body. `?bump=major\|minor\|patch` or `?version=`, `?notes=`, `?publish=false` stages it. The response's `advice` lists non-blocking suggestions | any |
| GET | `/spaces/{slug}/deployments` | 50 most recent deployments, newest first | any |
| GET | `/spaces/{slug}/versions` | Live and latest versions, next build and version suggestions, `contact_status` | any |
| GET | `/spaces/{slug}/contact` | Contact details (`status`: unknown, provided, skipped) | any |
| PUT | `/spaces/{slug}/contact` | Save contact fields, or `{"status":"skipped"}` | any |
| GET | `/deployments/{id}` | Deployment details and file list | any |
| POST | `/deployments/{id}/publish` | Make a deployment live (also rolls back) | any |
| POST | `/spaces/{slug}/artifacts` | Register an installer `{filename, version, build?, notes?, size, sha256}`; `201` with `upload` | any |
| GET | `/spaces/{slug}/artifacts` | Installers with `downloads` and `bytes_served` | any |
| GET | `/artifacts/{id}` | Installer details, with `upload` while pending | any |
| PUT | `/artifacts/{id}/content` | Upload an installer of up to 95 MiB in one request | any |
| PUT | `/artifacts/{id}/parts/{n}` | Upload part `n` (from 1) of a larger installer | any |
| POST | `/artifacts/{id}/complete` | Finish a multipart upload | any |
| DELETE | `/artifacts/{id}` | Remove an installer version (its URL returns 410) | any |
| POST | `/tokens` | Create a token `{name, space?}`; `space` limits it to one space | account |
| GET | `/tokens` | List tokens (values are never shown again) | account |
| DELETE | `/tokens/{id}` | Revoke a token | account |

"any" means an account token, or a space token for that space. `GET https://api.heremyapp.com/` without a token returns links to
these docs.

## Errors

```json
{"error":{"code":"unsupported_file_type","message":"Unsupported file types: notes.docx. ...",
 "details":{"files":["notes.docx"]},"docs":"https://heremyapp.com/llms-full.txt"}}
```

| Code | Status | Meaning |
|---|---|---|
| `unauthorized` | 401 | Missing, wrong, expired or revoked token |
| `https_required` | 403 | The request used plain `http://`; use `https://` (the token may have been exposed, so consider revoking it) |
| `forbidden` | 403 | Space token used for another space, or for an account-only action |
| `space_suspended` | 403 | The space has been suspended |
| `space_not_found`, `deployment_not_found`, `artifact_not_found`, `token_not_found` | 404 | Wrong slug or ID, or not yours |
| `not_found` | 404 | No such endpoint |
| `space_exists` | 409 | The space already exists; use it |
| `invalid_slug`, `invalid_json`, `invalid_artifact` | 400 | Fix the request body; `message` says how |
| `empty_body`, `invalid_archive`, `invalid_path`, `duplicate_path` | 400 | Fix the site archive; `message` names the file |
| `unsupported_entry` | 400 | The archive contains a symlink or special file; include real files instead |
| `unsupported_file_type` | 400 | Site contains files it cannot contain; see `details.files` |
| `missing_index` | 400 | No `index.html` at the site root |
| `site_too_large`, `too_many_files`, `upload_too_large` | 413 | Over the site limits |
| `checksum_mismatch`, `invalid_file_content` | 400 | Installer failed verification and was discarded; register again |
| `size_mismatch`, `invalid_part` | 400 | The upload sent a different number of bytes than expected |
| `length_required` | 411 | Send `Content-Length` (`curl -T` does) |
| `missing_parts` | 400 | Send the parts in `details.missing`, then complete again |
| `use_multipart`, `use_single_upload` | 400 | Wrong upload mode; `details.upload` has the right one |
| `artifact_exists`, `already_uploaded` | 409 | That version is already published; use a new version |
| `version_not_increasing`, `build_not_increasing` | 409 | Use a higher version or build; `details` has suggestions |
| `invalid_release` | 400 | Bad `bump` or `version` on a deploy |
| `deploy_conflict` | 409 | Another deploy finished at the same moment; deploy again |
| `invalid_contact` | 400 | Unknown field or invalid value; `details.allowed_fields` lists the fields |
| `internal_error` | 500 | Unexpected server error; retry later |

## Limits

| Resource | Limit |
|---|---|
| Site | 20 MiB of files, 1,000 files, 25 MiB compressed upload, paths up to 512 bytes |
| Installer | 500 MiB per file; single upload up to 95 MiB, larger files in 50 MiB parts |
| Installer types | .pkg, .dmg, .zip |
| Space slug | 1-30 characters: lowercase letters, digits, single hyphens |
| Account name | 3-30 characters: lowercase letters, digits, single hyphens |
