# Sporfie Public API — guide for AI agents

You are reading the agent-oriented entry point for the Sporfie Public API. It is one
self-contained document: everything needed to write a working integration is on this page, so
you should not need to crawl further.

- Machine-readable spec: **https://docs.sporfie.com/openapi.json** (OpenAPI 3.0, declares
  `bearerAuth`, so code generators emit the auth header correctly)
- Human reference: **https://docs.sporfie.com/**
- This page: **https://docs.sporfie.com/agents**

If you were given an API token and asked to "integrate with Sporfie", read the whole of
[Rules the OpenAPI spec does not tell you](#rules-the-openapi-spec-does-not-tell-you) before
writing code. Every item there is a failure that is easy to hit and confusing to debug.

---

## Facts to load first

| | |
|---|---|
| Base URL | `https://api.sporfie.com` |
| Auth | `Authorization: Bearer {token}` on every request |
| Format | JSON in, JSON out |
| Rate limit | ~1 request/second per token by default → `429` |
| All times | UTC, **milliseconds** since the Unix epoch, as numbers |
| Where secrets live | Server side only. Never in a browser, mobile app, or repo |
| Scope of a personal token | The organizations the token's owner **administers** |

---

## Prefer the MCP server if your client supports it

If you are an MCP-capable agent (Claude Code, Claude apps, Cursor, …) you do not need to write
HTTP calls at all: Sporfie ships an MCP server that wraps this API as 19 curated tools.

| | |
|---|---|
| Endpoint | `https://mcp.sporfie.com/mcp` (Streamable HTTP) |
| Auth | **OAuth 2.1** (one-click connect, recommended) **or** an `Authorization: Bearer {token}` header |
| Tools | events create/read/update/close/delete · search & live lookup · places & sports · highlight moments · event webhooks |
| Support | `search_help_center` / `get_help_article` / `list_help_center_sections` — no token needed |

**Two ways to authenticate, both fully supported:**

- **OAuth 2.1 (recommended for interactive clients).** Add the server with no header and the
  client runs the standard MCP OAuth flow (dynamic registration + PKCE): you sign in to Sporfie
  once in the browser, approve the connection, and the client stores and refreshes tokens
  automatically — no key to copy. This is what lights up the "Add custom connector" UI in
  Claude Desktop / claude.ai and the equivalent in other MCP clients.

  ```bash
  claude mcp add --transport http sporfie https://mcp.sporfie.com/mcp
  ```

- **Bearer header (for CLI / CI / scripts).** Create a personal token at
  `https://sporfie.com/settings/developer` and pass it directly — no browser needed. The same
  token also works for direct REST calls to `https://api.sporfie.com` (see below).

  ```bash
  claude mcp add --transport http sporfie https://mcp.sporfie.com/mcp \
    --header "Authorization: Bearer $SPORFIE_TOKEN"
  ```

Under the hood the OAuth flow mints the *same* kind of personal token, so either way you get a
credential scoped to the organizations you administer. Everything on this page stays true through
MCP — same identifiers, same token scope, same rate limits — because every tool call is the
corresponding REST call underneath.

---

## Authentication

Every request carries the token:

```
Authorization: Bearer eyJhbGciOi...
```

A user creates a token at **https://sporfie.com/settings/developer**. The secret is shown
**once**, at creation — it cannot be retrieved later, only revoked and replaced.

**Never call this API from client-side code.** The token grants everything its owner can do
across their organizations. It belongs in a server-side environment variable or secret manager.
If you are generating code, read it from the environment; do not inline it, and do not write it
into a file that could be committed.

### Verify the token before doing anything else

```bash
curl -sS -o /dev/null -w '%{http_code}\n' \
  -H "Authorization: Bearer $SPORFIE_TOKEN" \
  https://api.sporfie.com/public/sports
```

`200` means the token is live. `401` means it is wrong, revoked, or expired. Do this first: it
turns "my integration is broken" into a one-line answer.

---

## The domain in five objects

- **Organization** (called `company` in the API, `companyKey` in payloads) — the account that
  owns places and events. Almost every call is scoped to one.
- **Place** — a physical location that records: a court, a field, a rink. One event at a time.
- **Event** — one occurrence of an activity at a time and location: a match, a training session.
  Events may be attached to a Place (and then get cameras and streams) or exist standalone.
- **Moment** — a captured highlight inside an Event, with one or more video clips.
- **Uploaded Video Clip** — a video of **your own** that you put into Sporfie, as opposed to one
  Sporfie recorded. It belongs to the organization, not to any single event, and can be used
  wherever Sporfie takes a video of yours — as an event's highlight reel, for instance.

Two identifiers matter and are easy to confuse:

- **`eventKey`** — Sporfie's internal id, returned when you create an event.
- **`externalID`** — **your** id, which you choose. You set it as the path segment when creating
  an event, and afterwards either id works anywhere the docs say `eventKey`.

Using your own ids is the recommended pattern: your system stays the source of truth and you
never have to store a mapping table.

---

## What a token can reach

A personal token acts as its owner. It can operate on the organizations where that user is an
**admin or owner** — not organizations where they are merely a member, and not organizations
they have no relationship with.

A token may additionally be **restricted to specific organizations** at creation. That is a
narrowing, never a grant: the server checks the restriction *and* the owner's current
membership on every call. Two consequences worth designing for:

- The token you were given may reach fewer organizations than its owner administers. Never
  assume a `companyKey` will work because the person who gave you the token can see it —
  handle rejection per organization.
- Access can disappear without the token changing. If the owner leaves an organization, calls
  for it start failing while everything else keeps working. That is expected, not a broken
  token; do not retry it, and do not treat it as an auth failure.

Both of those come back as **`401`**, the same status as a bad token — the server does not
distinguish "your credential is wrong" from "your credential is fine but not for this
organization". So a `401` on its own tells you nothing. Re-check with `GET /public/sports`: a `200`
there means the token is live and the `companyKey` is what it cannot reach.

There is no per-endpoint scoping: within the organizations it can reach, the token can do
anything its owner can do. Treat it as a full credential.

If an integration needs to manage organizations the owner does not administer, a personal token
is the wrong tool — that requires a Sporfie-issued integration key. Tell the user to contact
Sporfie support rather than trying to work around it.

---

## Recipes

Every example assumes `SPORFIE_TOKEN` and `COMPANY_KEY` are set in the environment.

### Create an event

The path segment is **your** id for the event. The response gives you Sporfie's.

```bash
curl -sS -X POST "https://api.sporfie.com/public/events/my-match-2026-08-19" \
  -H "Authorization: Bearer $SPORFIE_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "companyKey": "'"$COMPANY_KEY"'",
    "name": "Under-16 semi-final",
    "sport": "volleyball",
    "location": { "name": "Municipal Hall", "geoLoc": { "lat": 50.8466, "lng": 4.3528 } }
  }'
```

```json
{ "eventKey": "-Nq7xK2mBv9pL3aRt8Zc" }
```

`companyKey` and `name` are always required. `location` is required **unless** you pass
`placeKey`. See the pitfalls section before adding scheduling fields.

### Create a scheduled event on a Place

Scheduling only exists for events attached to a Place. Find the place first:

```bash
curl -sS -H "Authorization: Bearer $SPORFIE_TOKEN" \
  "https://api.sporfie.com/public/places?companyKey=$COMPANY_KEY"
```

then create with all three fields together:

```json
{
  "companyKey": "...",
  "name": "Court 1 — evening session",
  "placeKey": "-NpQ...",
  "scheduledStartTime": 1755600000000,
  "scheduledEndTime": 1755607200000
}
```

Both times must be in the future, and the end must be at least **5 minutes** after the start.
`location` may be omitted — it is inherited from the Place.

### Find events

```bash
# by organization and state: future | current | past
curl -sS -H "Authorization: Bearer $SPORFIE_TOKEN" \
  "https://api.sporfie.com/public/event-search-by-company?companyKey=$COMPANY_KEY&state=future&pageSize=20&page=0"

# what is running on a place right now
curl -sS -H "Authorization: Bearer $SPORFIE_TOKEN" \
  "https://api.sporfie.com/public/places/$PLACE_KEY/activeEventKey"
```

`event-search-by-company` is paginated and returns a trimmed representation; fetch
`GET /public/events/{eventKey}` for the full object. Ordering depends on state: `future`
ascending by scheduled start, `past` descending by start.

### Push a score

```bash
curl -sS -X POST "https://api.sporfie.com/public/events/my-match-2026-08-19/score" \
  -H "Authorization: Bearer $SPORFIE_TOKEN" -H "Content-Type: application/json" \
  -d '{ "timeStamp": 1755600123456, "left": 12, "right": 9, "period": 2 }'
```

`timeStamp` is required. `left`/`right`/`period` are the generic shape; some sports carry a
richer `scoreValue` (tennis uses `gameScores` + `pointScore`, volleyball uses `gameScores`).
`timeStamp` and `key` are reserved names inside the score object.

You can also post to `POST /public/places/{placeKey}/score`, which routes to whatever event is
running there. **If nothing is running, the score is silently dropped** and you get a `warning`
field instead of keys — check for it.

### Capture a highlight

```bash
curl -sS -X POST "https://api.sporfie.com/public/events/my-match-2026-08-19/clicks" \
  -H "Authorization: Bearer $SPORFIE_TOKEN" -H "Content-Type: application/json" \
  -d '{ "timeStamp": 1755600123456 }'
```

Returns a `momentKey`. A `200` means the request was **accepted**, not that video exists —
clips take seconds to generate. Poll `GET /public/moments/{momentKey}` if you need to know when
they are ready.

While an event is open only the **last minute** of footage can be clipped, so call this close to
the action. Once an event has ended, the whole recording is available.

### Publish your own highlight reel

By default Sporfie compiles an event's highlight reel from the clips captured during the event.
If you would rather ship your own edit, upload it and attach it. Four steps:

```bash
# 1. announce the upload; you get a key and a URL to send the file to
curl -sS -X POST "https://api.sporfie.com/public/uploaded-video-clips" \
  -H "Authorization: Bearer $SPORFIE_TOKEN" -H "Content-Type: application/json" \
  -d '{
    "ownerType": "company",
    "ownerKey": "'"$COMPANY_KEY"'",
    "name": "Under-16 semi-final — our edit",
    "withPresignedURL": true
  }'
```

```json
{ "uploadKey": "-NrB4pQxW2LmT7dYs9Kv", "uploadURL": "https://s3.eu-west-1.amazonaws.com/..." }
```

```bash
# 2. send the bytes straight to S3 — no Sporfie auth header on this one
curl -sS -X PUT --upload-file our-edit.mp4 "$UPLOAD_URL"

# 3. report the transfer as finished; this is what starts Sporfie's processing
curl -sS -X PUT "https://api.sporfie.com/public/uploaded-video-clips/$UPLOAD_KEY" \
  -H "Authorization: Bearer $SPORFIE_TOKEN" -H "Content-Type: application/json" \
  -d '{ "uploadState": "DONE" }'

# 4. once processing has finished, attach the clip to the event
curl -sS -X PUT "https://api.sporfie.com/public/events/my-match-2026-08-19/reel" \
  -H "Authorization: Bearer $SPORFIE_TOKEN" -H "Content-Type: application/json" \
  -d '{ "uploadedVideoClipKey": "'"$UPLOAD_KEY"'" }'
```

**Step 3 is the one that gets skipped.** Sending the bytes does nothing on its own — the clip
sits in whatever state you announced it in until you report `DONE`.

Step 4 returns `422` until the video is ready. Poll `GET /public/uploaded-video-clips/$UPLOAD_KEY`
and wait for `videoURL` to appear (`upload.state` reaches `PROCESSED` at the same time).

The event's `highlightsURL` and `highlightsShareURL` then serve your video, and
`highlightsUploadedVideoClipKey` on the event names the clip. `DELETE` the same path to detach it;
the clip survives and stays yours. Attaching a clip also deletes any reel Sporfie had generated for
that event, so this is a replacement, not an addition.

For a file too large to `PUT` in one request, replace steps 1–2 with a multi-part upload: `POST`
`/public/multi-part-uploads?companyKey=$COMPANY_KEY` with `size` and `partSize`, `PUT` each chunk
to the matching entry of `partURLs` keeping each response's `ETag`, `POST` the complete endpoint
with those parts, then announce the clip with the `s3Region`/`s3Bucket`/`s3Key` you were given
instead of `withPresignedURL`.

### React to changes with a webhook

```bash
curl -sS -X PUT "https://api.sporfie.com/public/events/my-match-2026-08-19/watch" \
  -H "Authorization: Bearer $SPORFIE_TOKEN" -H "Content-Type: application/json" \
  -d '{ "url": "https://example.com/hooks/sporfie", "contentType": "json" }'
```

Both `url` and `contentType` are required, and `json` is the only supported content type.
Registering again replaces the previous webhook rather than adding a second one.

Sporfie then POSTs the full event object to your URL on every change. **The registration expires
after 12 hours** — re-register on a schedule or you will silently stop receiving events. `DELETE`
the same path to stop.

Prefer this over polling.

---

## Rules the OpenAPI spec does not tell you

These are the failures that cost the most time. Read them before writing code.

1. **`scheduledStartTime` / `scheduledEndTime` are rejected when there is no `placeKey`.** The
   prose reads as if they are generally optional; the server rejects them outright on a
   non-place event with *"scheduledStartTime and scheduledEndTime cannot be set for a non-place
   event"*. Either pass a `placeKey` and both times, or neither.

2. **Both scheduling times move together.** When present, both are required, both must be in the
   future, and the end must be ≥ 5 minutes after the start.

3. **`companyKey` is what authorizes the call**, not just metadata. A token that cannot act on
   that organization gets `401`, no matter how valid it is — the same status a revoked token
   gets. Do not conclude the token is dead until `GET /public/sports` also fails.

4. **The path segment on create is yours, not Sporfie's.** `POST /public/events/{X}` sets the
   event's `externalID` to `X`. It must match `[-_A-Za-z0-9]+`, be at most 64 bytes, and be
   unique for your token.

5. **Reusing an `externalID` is not idempotent.** If the event is still open you get `409`. If it
   was closed, the call **reopens that event** rather than creating a new one. Generate a fresh
   id per event unless reopening is what you want.

6. **`location` requires both `name` and `geoLoc`**, and `geoLoc` requires both `lat` and `lng`.
   A partial location is a `400`.

7. **Time is milliseconds, UTC.** Seconds-precision timestamps are a very common bug here and
   produce events dated 1970.

8. **`sport` must come from `GET /public/sports`.** An unknown code is a `400`. Omit it and you
   get the Place's sport, or `other`.

9. **Rate limiting is per token at about 1 request/second.** Bulk work needs pacing and a retry
   on `429` — do not parallelize a loop of creates.

10. **Score posted to a place with no active event is dropped, with a `200`.** The response
    carries `warning` instead of `eventKey`/`key`. Silent data loss if unchecked.

11. **Accepted ≠ done** for clicks, video generation and uploads. Poll if you need the artifact.

12. **Event watches expire after 12 hours.** Re-register.

13. **`PATCH /public/events/{eventKey}` has no required fields, whatever the spec says.** It
    reuses the `EventCreate` schema, so `openapi.json` lists `companyKey` and `name` as required
    and generated clients will demand them. The server does not: it updates only the fields you
    send. Send just what you want to change — resending `name` on a PATCH will overwrite the
    event's name.

14. **An upload is not finished until you say `uploadState: "DONE"`.** Transferring the bytes to
    the pre-signed URL, or completing a multi-part upload, changes nothing on Sporfie's side. The
    `PUT` to `/public/uploaded-video-clips/{uploadKey}` is what starts processing, and until
    processing ends the clip has no `videoURL` and cannot be used anywhere.

15. **Only the token that announced an upload can move it along.** `uploadState` and
    `uploadProgress` from any other token are **dropped silently, with a `200`** — the other fields
    in the same body are applied, so it looks like it worked. A token is identified by its owner
    rather than by the individual key, so rotating your key is fine; using a *different person's*
    token is not.

16. **A clip that is in use cannot be deleted.** While an Uploaded Video Clip is attached
    somewhere — as an event's highlight reel, say — `DELETE` returns `422` and names what is
    holding it. Detach first (`DELETE /public/events/{eventKey}/reel`), then delete. The clip's
    `refs` field tells you what is using it.

17. **API tokens can only own uploads under an organization or a Scope.** `ownerType` must be
    `company` or `scope` and is required — there is no default, because a token has no personal
    space to fall back on. And a clip may only become an event's reel if it is owned by that
    event's organization, that organization's Scope, or Sporfie; anything else is a `403`.

18. **An organization may hold at most 100 Uploaded Video Clips.** Announcement 101 is a `422`.
    The cap is per owner space, so a Scope has its own allowance. Delete what you no longer need.

---

## Errors

| Status | Meaning | What to do |
|---|---|---|
| `400` | A field is missing or invalid | Read the `errors` array; it names the field |
| `401` | Token missing, revoked or expired — **or** valid but not allowed to act on that `companyKey` | Verify with `GET /public/sports`. A `200` there means the token is fine and the organization is the problem: the owner must administer it **and** the token must not be restricted away from it |
| `403` | The object is outside what this token may touch, whatever organization it can reach | Not fixable by retrying. Most often an Uploaded Video Clip owned by a personal, brand or Sporfie space rather than by an organization |
| `404` | Object does not exist, or is not visible to this token | Check the key, then the scope |
| `409` | `externalID` already in use | Use a new id |
| `417` | Event still running | Close or end it before deleting |
| `422` | The object exists but is not in a state that allows this | Read the message: something must finish, be detached, or be under a limit |
| `429` | Rate limited | Back off and retry; ~1 req/s |

Do not retry `400`, `403`, `404` or `409` — they will never succeed unchanged. Retry `429` with
backoff, and `5xx` a few times. A `422` is worth retrying only when you are waiting on something
to finish, such as an upload still being processed.

---

## Building an integration

A workable order of operations:

1. **Verify** the token with `GET /public/sports`.
2. **Resolve the organization.** Ask the user for their `companyKey` (visible on their API tokens
   settings page) rather than guessing.
3. **Generate a typed client** from `https://docs.sporfie.com/openapi.json`. It declares
   `bearerAuth`, so generators wire the header for you.
4. **Read the token from the environment.** Never inline it, never commit it, never send it to
   the browser.
5. **Map your ids onto `externalID`** so your system stays the source of truth.
6. **Centralize the HTTP layer**: one wrapper handling the auth header, `429` backoff, and error
   decoding. Every recipe above goes through it.
7. **Use webhooks, not polling**, and refresh the registration inside 12 hours.
8. **Never log the token**, including in error paths.

If a call fails and you cannot tell whether it is auth, scope, or payload: re-run
`GET /public/sports` with the same token. That one call separates "bad token" from "wrong
organization" — both of which arrive as `401` — and the two have completely different fixes.

## Every endpoint

Generated from the OpenAPI spec (1.8.0). Full parameter and response detail is in
[openapi.json](https://docs.sporfie.com/openapi.json).

### Place data

| Method | Path | Purpose |
|---|---|---|
| `GET` | `/public/places` | Places |
| `POST` | `/public/places/{placeKey}/score` | Add a score entry in the score stream |

### Event data

| Method | Path | Purpose |
|---|---|---|
| `GET` | `/public/event-lookup-by-place-and-time` | Event lookup by place and time |
| `GET` | `/public/places/{placeKey}/activeEventKey` | Active Event on Place |
| `GET` | `/public/event-search-by-company` | Event search by company/organization |
| `GET` | `/public/events/{eventKey}` | Event data |
| `POST` | `/public/events/{eventKey}` | Create event |
| `PATCH` | `/public/events/{eventKey}` | Modify event |
| `DELETE` | `/public/events/{eventKey}` | Delete event |
| `POST` | `/public/events/{eventKey}/close` | Close an event |
| `POST` | `/public/events/{eventKey}/score` | Add a score entry in the score stream |
| `DELETE` | `/public/events/{eventKey}/score/{scoreKey}` | Delete a score entry |

### Event Watch

| Method | Path | Purpose |
|---|---|---|
| `PUT` | `/public/events/{eventKey}/watch` | Watch an event |
| `DELETE` | `/public/events/{eventKey}/watch` | Stop watching an event |

### Event highlights

| Method | Path | Purpose |
|---|---|---|
| `PUT` | `/public/events/{eventKey}/reel` | Set the highlight reel of an event |
| `DELETE` | `/public/events/{eventKey}/reel` | Remove the highlight reel of an event |

### Video generation

| Method | Path | Purpose |
|---|---|---|
| `POST` | `/public/overlay-preview` | Video Overlay Preview |
| `POST` | `/public/events/{eventKey}/clicks` | Click |

### Moment data

| Method | Path | Purpose |
|---|---|---|
| `GET` | `/public/moments/{momentKey}` | Moment |
| `PATCH` | `/public/moments/{momentKey}` | Update moment data |
| `DELETE` | `/public/moments/{momentKey}` | Delete a Moment |
| `DELETE` | `/public/moments/{momentKey}/clips/{clipKey}` | Delete a Video Clip |

### Bookmark management

| Method | Path | Purpose |
|---|---|---|
| `GET` | `/public/bookmark-collections/events/{eventKey}` | Bookmark Collections of Event |
| `POST` | `/public/bookmark-collections/events/{eventKey}` | Create Bookmark Collection for Event |
| `GET` | `/public/bookmark-collections/events/{eventKey}/{userID}` | Bookmark Collections of Event by User |
| `GET` | `/public/bookmark-collections/media-files/{mediaFileKey}` | Bookmark Collections of Media File |
| `POST` | `/public/bookmark-collections/media-files/{mediaFileKey}` | Create Bookmark Collection for Media File |
| `GET` | `/public/bookmark-collections/media-files/{mediaFileKey}/{userID}` | Bookmark Collections of Media File by User |
| `GET` | `/public/bookmark-collections/{bookmarkCollectionKey}` | Bookmark Collection |
| `PUT` | `/public/bookmark-collections/{bookmarkCollectionKey}` | Update Bookmark Collection |
| `DELETE` | `/public/bookmark-collections/{bookmarkCollectionKey}` | Delete Bookmark Collection |
| `GET` | `/public/bookmark-collections/{bookmarkCollectionKey}/shortURL` | Bookmark Collection Short URL |
| `POST` | `/public/bookmark-collections/{bookmarkCollectionKey}/export` | Export Bookmark Collection by Tag |
| `DELETE` | `/public/bookmark-collections/{bookmarkCollectionKey}/export/{exportKey}` | Delete Bookmark Collection Export |
| `GET` | `/public/bookmark-collections/{bookmarkCollectionKey}/bookmarks/{bookmarkKey}/shortURL` | Bookmark Short URL |
| `POST` | `/public/bookmark-collections/{bookmarkCollectionKey}/bookmarks` | Create Bookmark |
| `PUT` | `/public/bookmark-collections/{bookmarkCollectionKey}/bookmarks/{bookmarkKey}` | Update Bookmark |
| `DELETE` | `/public/bookmark-collections/{bookmarkCollectionKey}/bookmarks/{bookmarkKey}` | Delete Bookmark |

### Reel management

| Method | Path | Purpose |
|---|---|---|
| `GET` | `/public/companies/{companyKeyOrSlug}/reels` | Reels of a Company/Organization |
| `POST` | `/public/companies/{companyKeyOrSlug}/reels` | Create a Reel |
| `GET` | `/public/companies/{companyKeyOrSlug}/reels/{reelKey}` | Reel |
| `PUT` | `/public/companies/{companyKeyOrSlug}/reels/{reelKey}` | Update a Reel |
| `DELETE` | `/public/companies/{companyKeyOrSlug}/reels/{reelKey}` | Delete a Reel |
| `POST` | `/public/companies/{companyKeyOrSlug}/reels/{reelKey}/convertToVideoFile` | Convert a Reel to a video file |

### Uploaded content

| Method | Path | Purpose |
|---|---|---|
| `GET` | `/public/uploaded-video-clips` | Uploaded Video Clips of a Company/Organization |
| `POST` | `/public/uploaded-video-clips` | Announce an upload |
| `POST` | `/public/uploaded-video-clips-from-image` | Create an Uploaded Video Clip from a still image |
| `GET` | `/public/uploaded-video-clips/{uploadKey}` | Uploaded Video Clip |
| `PUT` | `/public/uploaded-video-clips/{uploadKey}` | Update an Uploaded Video Clip |
| `DELETE` | `/public/uploaded-video-clips/{uploadKey}` | Delete an Uploaded Video Clip |

### Multi-part uploads

| Method | Path | Purpose |
|---|---|---|
| `POST` | `/public/multi-part-uploads` | Start a multi-part upload |
| `POST` | `/public/multi-part-uploads/{uploadId}/complete` | Complete a multi-part upload |

### Scope data

| Method | Path | Purpose |
|---|---|---|
| `GET` | `/public/scopes/{scopeKey}/companies` | Companies of a Scope |

### Sport data

| Method | Path | Purpose |
|---|---|---|
| `GET` | `/public/sports` | Sports |


## Request bodies at a glance

Required fields for every operation that takes a JSON body. Sending a body without these
returns `400`.

| Method | Path | Required | Also accepts |
|---|---|---|---|
| `POST` | `/public/places/{placeKey}/score` | `timeStamp` | — |
| `POST` | `/public/events/{eventKey}` | `companyKey`, `name` | `description`, `notSearchable`, `placeKey`, `scheduledStartTime`, `scheduledEndTime`, `thumbnailURL`, `homeTeam`, `awayTeam`, `sport`, `pinCode`, `cameraPinCode`, `location`, … |
| `PATCH` | `/public/events/{eventKey}` | — *(partial update)* | `companyKey`, `name`, `description`, `notSearchable`, `placeKey`, `scheduledStartTime`, `scheduledEndTime`, `thumbnailURL`, `homeTeam`, `awayTeam`, `sport`, `pinCode`, … |
| `POST` | `/public/events/{eventKey}/score` | `timeStamp` | — |
| `PUT` | `/public/events/{eventKey}/watch` | `url`, `contentType` | — |
| `PUT` | `/public/events/{eventKey}/reel` | `uploadedVideoClipKey` | — |
| `POST` | `/public/overlay-preview` | `overlays` | `sourceURL` |
| `POST` | `/public/events/{eventKey}/clicks` | `timeStamp` | `startTime`, `endTime`, `score`, `overlays`, `players`, `metadata` |
| `PATCH` | `/public/moments/{momentKey}` | — *(partial update)* | `score`, `players`, `metadata` |
| `POST` | `/public/bookmark-collections/events/{eventKey}` | `name`, `isPublic` | — |
| `POST` | `/public/bookmark-collections/media-files/{mediaFileKey}` | `name`, `isPublic` | — |
| `PUT` | `/public/bookmark-collections/{bookmarkCollectionKey}` | `name`, `isPublic` | — |
| `POST` | `/public/bookmark-collections/{bookmarkCollectionKey}/export` | `name`, `tag`, `mediaFileKey` | — |
| `POST` | `/public/bookmark-collections/{bookmarkCollectionKey}/bookmarks` | `startTime` | `endTime`, `tag`, `comment`, `thumbnailBase64` |
| `PUT` | `/public/bookmark-collections/{bookmarkCollectionKey}/bookmarks/{bookmarkKey}` | — | `startTime`, `endTime`, `tag`, `comment`, `thumbnailBase64` |
| `POST` | `/public/companies/{companyKeyOrSlug}/reels` | `name` | `eventKey`, `thumbnail`, `thumbnailURL`, `playlist` |
| `PUT` | `/public/companies/{companyKeyOrSlug}/reels/{reelKey}` | `name` | `eventKey`, `thumbnail`, `thumbnailURL`, `playlist` |
| `POST` | `/public/uploaded-video-clips` | `ownerType`, `ownerKey` | `name`, `withPresignedURL`, `s3Region`, `s3Bucket`, `s3Key`, `duration`, `size`, `orientation`, `width`, `height`, `shouldCrop`, `thumbnail`, … |
| `POST` | `/public/uploaded-video-clips-from-image` | `ownerType`, `ownerKey`, `thumbnail`, `duration` | `name`, `orientation`, `width`, `height`, `shouldCrop`, `thumbnailSizes`, `source` |
| `PUT` | `/public/uploaded-video-clips/{uploadKey}` | — | `uploadState`, `uploadProgress`, `error`, `name`, `thumbnail`, `thumbnailSizes` |
| `POST` | `/public/multi-part-uploads` | `name`, `size`, `partSize` | `type`, `contentDispositionValue` |
| `POST` | `/public/multi-part-uploads/{uploadId}/complete` | `s3Bucket`, `s3Key`, `parts` | `s3Region` |


## Sport codes

Valid values for an event's `sport`. Also available live at `GET /public/sports`.

`ballhockey` · `baseball` · `basketball` · `beachvolleyball` · `billiard` · `bowling` · `cheer` · `cricket` · `dekhockey` · `football` · `frontenis` · `futsal` · `golf` · `handball` · `hockey` · `lacrosse` · `other` · `padel` · `pickleball` · `pongbeyond` · `rollerderby` · `rugby` · `soccer` · `softball` · `swimming` · `tennis` · `unihockey` · `volleyball` · `waterpolo` · `wrestling`


---

*Generated from the Sporfie OpenAPI spec, version 1.8.0. The spec is the
source of truth: when this page and [openapi.json](https://docs.sporfie.com/openapi.json) disagree,
believe the spec and please report the drift.*

