Skip to content
View as Markdown

Guide for coding agents

This page is written to you, the coding agent asked to integrate SocialScope into an existing application. It tells you what to read, what to inspect in the app first, the rules you must not break, the order to build things in, what to test and what to report. SocialScope gives the app hosted OAuth for YouTube, TikTok and Instagram plus observed account data over one server-to-server GraphQL API, and optionally Google sign-in. The app keeps its own backend, users, sessions and database. No SDK or npm package is needed.

1. Load the contract

Read these before writing code. They are plain Markdown.

Need URL
Index of every page https://docs.socialscope.hardscope.incdev.dev/llms.txt
The 5-minute path https://docs.socialscope.hardscope.incdev.dev/quickstart.md
Connect flow and outcomes https://docs.socialscope.hardscope.incdev.dev/connect.md
Reading data https://docs.socialscope.hardscope.incdev.dev/reading-data.md
Google sign-in https://docs.socialscope.hardscope.incdev.dev/google-identity.md
Every error code https://docs.socialscope.hardscope.incdev.dev/errors.md
Every operation https://docs.socialscope.hardscope.incdev.dev/api-reference.md
GraphQL schema https://docs.socialscope.hardscope.incdev.dev/schema.graphql

Use only operations and fields that exist in the schema. If you cannot fetch the docs or the schema, finish the inventory below and report that blocker. Do not invent operations or fields.

Validate every GraphQL document you write against /schema.graphql locally, for example with graphql-js buildSchema and validate in a unit test. SocialScope answers an invalid query with a bare BAD_USER_INPUT that does not name the bad field.

2. Inventory the app before designing

Find and write down, with file paths:

  • Server routes or server actions, and how the app protects them against CSRF.
  • How a signed-in user is identified on the server, and the stable user ID to use as externalSubjectId.
  • How the current workspace is selected and authorized, to map to tenantKey. If the app has no workspaces, it uses one fixed tenant key from server configuration.
  • The secret store and how server environment variables are read.
  • The existing HTTP or API client conventions, logging and error reporting.
  • The database and migration tool, for the attempt records you need to store.
  • The test framework and how routes are tested.

Reuse all of these. Do not add a new auth system, database, queue or framework for this integration.

Then pick the flows the task asks for: social account connection, Google sign-in, or both. They are separate. Google sign-in grants no YouTube access, and connecting YouTube signs nobody in.

3. Rules

You must follow every rule in this list.

Secrets

  1. Keep the application key server-side only, in the app's secret store, as SOCIALSCOPE_CONSUMER_KEY, next to SOCIALSCOPE_GRAPHQL_URL.
  2. Never put the key in NEXT_PUBLIC_*, VITE_* or any client-bundled variable, browser storage, a mobile bundle, source control, test fixtures or logs.
  3. Never log the Authorization header, a launchToken, a resultProof or raw SocialScope responses.
  4. Use only the key issued to your app.

Ownership

  1. Derive tenantKey and externalSubjectId from the server session on every call. Never accept them from a form field, query string, header or request body the browser controls.
  2. The key identifies the app. There is no app ID argument. Never pass or trust an app ID from the browser.
  3. Connections are separate per app, tenant and subject. Do not try to share them between apps.

Connect

  1. Call integrationReadiness before offering a provider. Show the action as unavailable when the provider is false or the call fails. Never bypass readiness.
  2. Create the attempt with createConnectSession in an authenticated, CSRF-protected server action, and save its id with the user, tenant and provider before sending the browser anywhere.
  3. Check launchUrl is on the Connect host (the origin of SOCIALSCOPE_GRAPHQL_URL) with path exactly /api/connect/redeem and no query or fragment.
  4. Serve a no-store HTML page from the origin of the return URL that auto-submits a top-level form posting only launchToken. Escape both values. Send Referrer-Policy: strict-origin. Never put the token in a URL and never send it from the server.
  5. On return, treat ss_session_id as a hint only. Require the same signed-in user and tenant, match an unused saved attempt, mark it used, then call connectSessionResult with the saved scope and ID.
  6. Show success only when the result is COMPLETED, its IDs match, and connection.status is CONNECTED. Present posts by syncStatus: PENDING or RUNNING is loading, READY is loaded, PARTIAL is loaded but possibly incomplete, STALE shows the stored data with its age, FAILED is an error with a retry.
  7. Show the "remove the app in the provider's settings" instruction whenever creatorAction is set, including values you do not recognize.
  8. On denial, expiry, failure or SocialScope downtime, offer a retry. Never infer success and never fall back to the app's own provider OAuth.
  9. Remove ss_session_id and ss_identity_id from the URL before analytics or third-party scripts load.
  10. The session cookie checked on the return routes must be SameSite=Lax or None, because SocialScope reaches them with a cross-site redirect.
  11. In production, FORBIDDEN from createConnectSession and false readiness can be transient while a YouTube or TikTok disconnect settles. Retry later before treating it as misconfiguration.

Data

  1. Never show an unavailable metric as 0. Only availability: AVAILABLE carries a value.
  2. Treat DecimalCount as an integer string. Do not parse it into a float.
  3. Show observedAt next to counts. Keep sourceField when you interpret them.
  4. Do not claim a list is complete unless pageInfo.completeForWindow is true for that exact window.
  5. Refresh and lookup are asynchronous and there are no webhooks. Poll syncJob before reading again. After a lookup succeeds, read contentItem and check its availability.
  6. Store the connection IDs you receive in your own database and read each with connection. A CONNECTED account can still fail its read with AUTHORIZATION_SUSPENDED, RECONNECT_REQUIRED, CAPABILITY_UNAVAILABLE or FORBIDDEN, and one such account fails the whole connections list. Map each code to that account's state.
  7. Do not tell a person a disconnect revoked access at the provider unless the revoke receipt is SUCCEEDED with a null creatorAction. Instagram never confirms a revoke.

Google identity

  1. Bind createGoogleIdentitySession to the browser session. Do not require an existing local user.
  2. Keep resultProof on the server. Redeem once, with the saved tenant and proof, and only for the browser session that started the attempt.
  3. Create or find the local user only on COMPLETED with non-empty issuer and googleSub, keyed by that pair. Never link accounts by email. Check emailVerified.
  4. Create the app's session only after the user write succeeds.

Scope of your work

  1. Registering the app, issuing keys and enabling providers or Google identity are done by a SocialScope admin. List what the admin needs. Do not attempt it yourself.
  2. A disabled readiness value is not permission to widen access or work around it.

4. Build order

  1. Add the server-only helper from Authentication and the two environment variables.
  2. Add the readiness check and render provider actions from it.
  3. Add the attempt store: a table or existing session store holding attempt ID, user ID, tenant key, provider, resultExpiresAt and a used flag.
  4. Add the start action and the launch page from Connect.
  5. Add the return route with the full verification from Connect.
  6. Add the reads the feature needs from Reading data, with the availability and completeness rules.
  7. Add reconnect and disconnect if the feature needs them.
  8. Add Google identity only if asked, following Google identity.
  9. Write the tests below as you go, one behavior at a time.

5. Tests to write

Use the app's own test tools. Stub SocialScope at the HTTP boundary with responses shaped like the schema. See Testing for an example.

  • Two users: neither can read the other's connections.
  • Two app keys: the second app cannot read the first app's connections.
  • A foreign, duplicate, malformed or used ss_session_id is rejected without showing success.
  • An unavailable provider in readiness renders as unavailable. A failed readiness call does too.
  • Provider denial and expiry produce a retry.
  • COMPLETED with a connection that is not CONNECTED is not shown as connected.
  • A metric that is not AVAILABLE renders as "not available", not 0.
  • For Google identity: a new user, a returning user, and a rejected second redemption.
  • The key does not appear in client bundles.

A stubbed suite and SocialScope's local fixture prove wiring, not provider approval or a live consent.

6. Report back

When you finish, report:

  • The routes and files you added or changed.
  • The admin registration the app needs, without secret values: slug, exact origins, return URLs, tenant keys, providers, capabilities, and the Google identity return URL if used.
  • The server settings to add: SOCIALSCOPE_GRAPHQL_URL and SOCIALSCOPE_CONSUMER_KEY, with no values.
  • The checks you ran and their results.
  • What still needs a live check: one real account connected per provider on the target environment, and a real Google sign-in if used.
  • Any part of the contract you could not read, and the date you read the docs.