IAMScouting docs

IAMScouting Public REST API (v1)

The /api/v1/* surface lets third-party services and clubs read their own

data and post programmatically from servers, scripts, and CRMs. It is the

public, machine-readable counterpart of the in-app /api/network/* routes

(which are session-cookie authenticated and intended for the browser).

> Status: v1 stable. Breaking changes will introduce a /api/v2/* rather

> than mutate v1 in place.

---

Authentication

Every /api/v1/* request must carry a bearer token in the Authorization

header:

Authorization: Bearer iams_live_<your_32_hex_key>

Generate keys at /account/api-keys. Each key:

a secrets store, never in client-side code or a public repo

immediately

There is no refresh / rotation endpoint — to rotate, create a new key, swap

it in, then revoke the old one.

Scopes

| Scope | What it grants |

|-------|----------------|

| read | GET endpoints (list / read your own data). Required minimum. |

| write | POST / PATCH / DELETE on your own data. |

| admin | Implicit superset of read + write. Use sparingly. |

A request to a route that needs write with a key that only has read

returns 403 insufficient_scope — the response includes required_scope

and your_scopes so the integration can surface a clear error.

---

Rate limits

Every request runs through a single shared bucket per key:

| Limit | Window | Bucket |

|-------|--------|--------|

| 1000 requests | per hour (rolling 3600 s) | api_v1, keyed on the API key id |

When a key exceeds the limit, the response is:

HTTP/1.1 429 Too Many Requests
Retry-After: 1832
Content-Type: application/json

{ "error": "rate_limited", "retry_after_seconds": 1832, "limit": 1000, "bucket": "api_v1" }

Honour Retry-After; back off until the next window. Need a higher ceiling?

Email start@iamscouting.com and we'll discuss a partner SLA.

---

Error codes

All errors are JSON with an error (machine code) and usually a detail

(human message).

| HTTP | error | When |

|------|---------|------|

| 400 | invalid_json | Body wasn't valid JSON. |

| 400 | invalid_<field> | A specific field failed validation (see detail). |

| 401 | unauthorized | Missing / malformed / unknown / revoked bearer token. |

| 403 | insufficient_scope | Key lacks the required scope. |

| 403 | agent_only / agent_not_verified | Lead endpoints require a verified agent persona. |

| 403 | upgrade_required | Posting requires a paid subscription tier. |

| 403 | *_limit_reached | You're at your tier's active-object cap. |

| 404 | not_found | The id doesn't exist. |

| 404 | user_not_found | /api/v1/me couldn't resolve the user (unusual). |

| 429 | rate_limited | See above. |

| 429 | pin_limit_reached | Active-pin cap reached (scout tier dependent). |

| 500 | server error | Something failed inside the API. |

---

Endpoints

All examples assume:

export IAMS_KEY="iams_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
export IAMS_BASE="https://iamscouting.com/api/v1"

GET /api/v1/me

Return the authenticated user's profile.

curl -s "$IAMS_BASE/me" \
  -H "Authorization: Bearer $IAMS_KEY"

Response

{
  "id": "uuid",
  "email": "you@club.com",
  "name": "Your Name",
  "avatar_url": null,
  "persona": "agent",
  "display_handle": "agent-xyz",
  "country": "DE",
  "agent_verified": true,
  "trust_score": 72,
  "scopes": ["read", "write"]
}

Required scope: read.

---

GET /api/v1/leads

List your own leads (any status). Optional ?status=published|paused|closed.

Optional ?limit= (1–200, default 50).

curl -s "$IAMS_BASE/leads" -H "Authorization: Bearer $IAMS_KEY"

Returns { "leads": [ … ] }. Required scope: read.

POST /api/v1/leads

Create a new lead. Body schema mirrors the in-app endpoint

(/api/network/leads POST):

curl -s "$IAMS_BASE/leads" \
  -H "Authorization: Bearer $IAMS_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "player_name": "Alex Example",
    "position": "AM/RW",
    "age": 21,
    "current_club": "FC Example",
    "ask_price_min": 1500000,
    "ask_price_max": 2500000,
    "exclusivity": "exclusive",
    "visibility": "pro",
    "expires_in_days": 21,
    "notes": "Available January window."
  }'

Returns { "ok": true, "lead": { … } }. Required scope: write.

Caller must be a verified agent (persona = agent + agent_verified_at set)

on at least Starter tier. Tier caps + roster-match rules apply identically

to the in-app route.

---

GET /api/v1/jobs

List your own job listings. Same shape + params as /api/v1/leads.

POST /api/v1/jobs

Create a job listing. Body mirrors /api/network/jobs POST: club_name,

role_title required; role_type, location, country, salary_min,

salary_max, description, requirements, apply_deadline, visibility,

expires_in_days optional.

Required scope: write. Starter tier minimum.

---

GET /api/v1/pins

List your scouting-trip pins. Optional ?limit= (1–500, default 100).

POST /api/v1/pins

Create a pin.

curl -s "$IAMS_BASE/pins" \
  -H "Authorization: Bearer $IAMS_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "dest_city": "Porto",
    "dest_country": "Portugal",
    "start_date": "2026-08-12",
    "end_date": "2026-08-14",
    "identity_tier": "T1",
    "note": "Watching FC Porto B vs. Estoril."
  }'

Required scope: write. Subject to your scout-tier active-pin cap.

PATCH /api/v1/pins/{id}

Edit one of your own pins. Body accepts any subset of pin fields. Returns

{ "ok": true }. Required scope: write.

DELETE /api/v1/pins/{id}

Delete one of your own pins. Required scope: write.

---

GET /api/v1/watchlist

List your match watchlist rows.

POST /api/v1/watchlist

Add a fixture.

# Smrt-source fixture
curl -s "$IAMS_BASE/watchlist" \
  -H "Authorization: Bearer $IAMS_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "source": "smrt", "match_id": 12345, "priority": 2 }'

# External-source fixture
curl -s "$IAMS_BASE/watchlist" \
  -H "Authorization: Bearer $IAMS_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "source": "football_data", "external_id": "match_id_from_provider" }'

Valid source: smrt, football_data, wikidata, ugc, openligadb,

openfootball. Smrt-source rows require a numeric match_id; all other

sources require external_id.

Required scope: write.

DELETE /api/v1/watchlist?id={watchlist_row_uuid}

Remove a row. Required scope: write.

---

Key management (not part of /api/v1)

Key CRUD is session-cookie authenticated, not API-key authenticated. You

manage keys from a browser while signed in, then hand the keys to your

integration:

includes a one-time full_key field — store it now or lose it.

These three routes also live behind the normal app middleware (auth, CSRF

posture identical to other /api/* browser routes) and are NOT subject to

the /api/v1 rate-limit bucket.

---

Webhooks

Not yet. v1 is pull-only — your integration polls the relevant GET

endpoint, or subscribes to in-app alerts at /network/alerts. Outbound

webhooks are on the roadmap (track in ROADMAP.md).

---

Versioning

validation rule constitutes a break and will ship as v2 with v1

remaining live in parallel for at least 6 months.

has ever created an API key.