MCP

Connect Spotka to your AI app

Spotka speaks MCP.

Add one line to Claude, Cursor, or any app that speaks MCP, and it can find things to do, make events, and answer invitations for you, in your name, with your own account.

The endpoint

https://spotka.co/api/v1/mcp

It speaks MCP over plain HTTP: one POST per message, which is what Claude, Cursor and Windsurf send. Your key travels in an X-API-Key header.

There is nothing to install and no server to run. Two protocol versions are supported, 2025-06-18 and 2025-03-26; your app picks one on its own.

First, get your key

Open Spotka on the web, Android or iPhone and go to Settings → API access → Create a key, or jump straight there:

Open Spotka settings

You get a short key like spk_4KAS22M1JQFQ. It is shown once. If you lose it, make a new one. It takes one tap, and you can revoke the old one on the same screen.

A key belongs to one person: yours. Whatever you connect can do only what you can do in the app yourself.

Add it to your app

Claude Code

claude mcp add --transport http spotka https://spotka.co/api/v1/mcp \
  --header "X-API-Key: spk_XXXXXXXXXXXX"

Claude Desktop, Cursor, Windsurf and most others

These keep a small config file listing the MCP servers they use. Add Spotka to it:

{
  "mcpServers": {
    "spotka": {
      "type": "http",
      "url": "https://spotka.co/api/v1/mcp",
      "headers": { "X-API-Key": "spk_XXXXXXXXXXXX" }
    }
  }
}

Then restart the app. Spotka's tools show up in its tool list.

An app that only lets you paste a URL

Some apps have nowhere to put a header. Put the key in the URL instead. It works exactly the same:

https://spotka.co/api/v1/mcp?key=spk_XXXXXXXXXXXX

Treat that URL like a password, because it now contains one.

Check it worked: open https://spotka.co/api/v1/mcp in a browser. It answers with the server name and the list of tools, no key needed.

What your app can then do

ToolWhat it does
ideasThings to do in the city, picked for you
eventsEvents other members are hosting that suit you. Text search can find public events beyond your current suggestions. Filter with query or its alias search, and bound dates with date_from/date_to or starts_after/starts_before
my_eventsEvents you host, events you joined, invitations waiting for you
create_eventAdd an event. Title up to 120 characters; longer is rejected, not cut short. about up to 1000 characters; longer is rejected, not cut short. Angle brackets in about are preserved, not stripped. max_guests is 2–8; out of range is rejected. capacity is accepted as the same field (the name every event read returns). when may be a weekday phrase, an ISO instant, a date-and-time with no offset (2026-09-26 18:00 is 18:00 Europe/Warsaw, the same as typing 6pm), or a dated wall-clock with an IANA zone (2026-09-10 12:00 Europe/Warsaw); starts_at/ends_at may be sent as direct ISO fields instead. An unreadable time is time_not_applied, not a guessed date. A bare number (a Unix timestamp) is time_not_applied, not converted to a date. A UTC offset no civil timezone uses (+15:00) is time_not_applied, not converted with arithmetic. Leave when and starts_at out to leave the draft with no time rather than inventing a start. where is a known public-place name, JSON with lat/lng, or later to decide with guests. A title-only create stays a draft: publishing needs a time and a place, the same as update_event
update_eventChange an event you host: the event must be the full event id; a truncated id is invalid_event_id. Partial, so anything you leave out stays as it is. Title up to 120 characters; longer is rejected. about up to 1000 characters; longer is rejected. Angle brackets in about are preserved, not stripped. If the supplied time cannot be applied, or publish=true cannot complete, the call fails and the other fields are not changed. A when-less and starts_at-less draft stays with no time rather than inventing a start, and cannot be published until one is set. Same where shapes as create. On a published event, where= is place_not_applied (the place is fixed while it is live), not invitation_not_draft
inviteInvite people to an event, or make the event and invite in one go. Success means a pending invitation the guest can see and answer, not just a green reply. A truncated event id is invalid_event_id, and one that does not exist is event_not_found. Neither sends anything, and neither uses any of your daily recipient allowance
eventOne event in full: lifecycle, held seats vs calendared guests, guest states, seats remaining, allowed actions. The event must be the full event id; a truncated id is invalid_event_id
joinTake a seat at someone else's event, at the time the host set. An event with no time yet is time_not_set
leaveGive up a seat you took. A host cannot leave their own event (cannot_leave_own_event). Archive or delete it instead. A missing event is event_not_found, not candidate_stale. The rest of the member's event suggestions stay
respondAccept, decline, or interested. Accept takes a seat; interested is a soft RSVP that records the person on the host's interested list without taking a seat. Changing interested to accept takes a seat; it is not already_responded. Accepting an event with no time yet is time_not_set. A never-answered invite on an archived event is event_archived (the host retired it), not already_responded. Repeating the same decision is a no-op success, not already_responded. An id that is not an event is event_not_found
archiveRetire an event you host. The event must be the full event id; a truncated id is invalid_event_id. An event you cannot see is event_not_visible, not invitation_not_found
delete_eventDelete an unpublished draft. The event must be the full event id; a truncated id is invalid_event_id. Already gone is event_not_found. An event you cannot see is event_not_visible, not invitation_not_found
inboxWhat is waiting: unread conversations and invitations. unread_total is global, not the page
conversationOne conversation, oldest first, without marking it read. oldest_at is the cursor for before=
mark_readMark a conversation read, on purpose
send_messageSend a message as you. Needs a key with messaging enabled
activity_historyWhat this API changed on your account, including keys you minted or revoked. succeeded=false lists only failed actions. verb narrows to one action; only actions that change something are recorded, so a read verb is refused and the error lists what you may filter by
revoke_inviteTake back an invitation you sent. The id do=event lists is enough on its own
calendarWhat you have coming up, hosted and joined
profileYour own profile: public half and private context
update_profileChange your own profile; only the fields you name. display_name and first_name cannot be blank. Names refuse line breaks and control characters. Overlong text (bio up to 2000 characters) is rejected, not shortened. Over-long lists are rejected, not cut short (interests 40, dietary_preferences and allergies 20, languages 12). Each interests / dietary_preferences / allergies item is up to 120 characters; longer is rejected, not sliced. languages must be on the app list; invented words are invalid_languages, not stored. personal_url must be a public-page slug (invalid_personal_url). instagram, linkedin and telegram must be a handle or profile URL (invalid_social_handle). HTML tags on bio, city, company, job_title, community_goals, current_focus, allergies, linkedin and personal_url are stripped on save, the same way event titles do, rather than stored raw. List fields parse a JSON array string; invalid JSON is refused, not comma-split
notification_settingsWhich notifications you get by push and by email, and whether any device is registered to deliver them
update_notification_settingsTurn notifications on or off, on one rail or both; only the ones you name change
report_bugTell the Spotka team something is broken. Default is no auto-fix. Send autofix=true on a clear bug to send it to a coder. Duplicates fold onto the open report they match; send check_only=true to get that verdict without filing. Pass product_url, app_version, platform, and a screenshot when you have them. If you are an AI agent, send agent with your own name: the account is a shared seat and does not say who wrote the report. Poll report_status; when waiting for a re-test, call verify_bug
report_featureAsk for something Spotka does not do yet: same queue, same rule
report_statusPoll a report this account filed. Omit report to list recent ones. A fleet agent may pass waiting=true to list actionable reports waiting for a re-test; each item carries verify_surface. Pass surface=api/web/ios/android/site/intranet/person with waiting=true to fetch only that lane's queue, or tier to fetch only one tester's work
verify_bugUse probe=true with note and dataset_version to save an own-report observation at any time or contest a closure: still_broken reopens it; works outside the queue saves evidence without verifying a fix. Normally, submit the retest verdict without the email token. verdict=works, still_broken or unverified. A fleet agent may also verify any waiting report. A cascade tester sends tier and reason, so an unverified answer passes the report to the next tester
capabilitiesWhat your key may do, what each tool changes in the world, and how much budget is left

If this key belongs to a test account, report_bug and report_feature file as soon as you observe a problem. Do not wait for a person to confirm. Those reports show up in the review queue tagged as a test filing. Default is no auto-fix. Send autofix=true on a clear bug to send it to a coder. Poll report_status; when waiting for a re-test, call verify_bug. Do not wait on email.

Times and places are ordinary words, or an ISO instant. Your app should say next Friday at 7pm, 2026-08-22T18:00:00+02:00, or 2026-09-26 18:00 (18:00 Europe/Warsaw). If the time cannot be read, or publishing cannot complete, the tool fails and names the unmet fields rather than leaving a silent draft.

A few more tools exist for writing test suites and work only with a test key: actors, seed, reset, key_list, key_create and key_revoke. They are described in the full API guide.

Try it

Once it is connected, just ask in ordinary words:

  • "What's on in Warsaw this weekend?"
  • "Any Spotka events I could join on Saturday?"
  • "Make an event: board games, Sunday at 6pm, Kawiarnia Relaks."
  • "Do I have any invitations waiting?"
  • "Something broke while I was testing. File a bug report."

If you would rather not use MCP at all, the same things work with one curl line. See the full API guide.

Good to know

Invitations send real email

They go out in your name, so it is worth telling your assistant to check with you before it invites anyone.

Wrong parameter names

A name a tool does not take comes back as unknown_parameter, naming the extra key, rather than being ignored. When the guess is close, the hint offers the right name (q → query on events). The call does not run.

Reporting a problem while testing

If you are signed in as a test or bot account, file with report_bug or report_feature as soon as something is wrong. Do not wait for a person to confirm. The report is tagged as a test filing. Default is no auto-fix. Send autofix=true on a clear bug to send it to a coder. Feature requests still wait for a person.

A bot cannot click the verification email. Poll report_status, re-test on production when waiting_for_retest is true, then call verify_bug with verdict=works or still_broken. If you could not run the re-test at all, send verdict=unverified, which leaves the report waiting instead of marking a fix wrong. A QA fleet agent may also pass waiting=true on report_status to pick up reports another person filed, then verify those after a real check. Do not wait for the original reporter. Do not read the mailbox.

Limits and speed

Same key, same limits as the rest of the API: a per-minute and per-day call budget, a tighter one for events and invites, and a daily cap on people invited. What you have left is in the X-RateLimit-Remaining header of every reply, and X-RateLimit-Bucket names which budget it is counting: always the one closest to running out, usually the per-minute one.

The numbers, and how long each call takes, are on the full API guide, kept in one place on purpose, so the two pages cannot quietly disagree about them. If you need more, write to support@spotka.co.

If your app cannot connect

  • The endpoint answers POST. There is no server-to-client stream, so a client that insists on one is told so plainly rather than left hanging.
  • A key that is missing or wrong shows up on the first real call, not at connect time. The tool list is public on purpose.
  • Still stuck? Write to support@spotka.co.

Hosted assistant connections

Hosted assistants can use the member-only OAuth endpoint https://spotka.co/api/v1/connector/mcp. Sign in and choose permissions when your assistant asks to connect. Disconnect through Spotka Settings → API access. Connector writes require an operation ID for safe retries.

Meta Muse and OpenAI Dots platform review is pending. The API-key connection above remains available.

Report status separates fix_merged (claimed fix) from fix_verified (successful re-probe). fix_landed is a compatibility alias for verified. API-only reporters do not need an email address to enter the retest queue. The default fleet queue excludes person-only, postponed and closed reports. Fetch waiting=true with your tier; request surface=person or tier=person explicitly to inspect work assigned to a person.