Get started
Make your first request
Connect a verified agency, link a creator, and follow a real scan from request to findings.
On this page
1. Verify your agency and create a key
Create a CRD agency account at /create-agency and complete onboarding and the existing agency KYC process. After approval, the owner can open /agency/api to provision the agency’s API account automatically. There is no separate API approval or manual enablement step.
Sign in as the agency owner and open /agency/api. Create a key with the permissions your integration needs. Save the complete crd_live_ secret in your server’s secret store when it is shown; it cannot be displayed again.
A key_… value identifies a credential in the dashboard. It cannot authenticate requests. Use the complete one-time secret; create a replacement if it was lost.
2. Confirm your connection
Set CRD_API_KEY in your backend environment. The examples call https://api.crd.systems; to try them against a copy running on your own computer, set CRD_BASE_URL. Call GET /v1/me to check the credential, environment, assigned workspace, and engine configuration.
Use a key with workspaces:read and save the assigned workspace ID. If configuration is unavailable, have CRD restore it before creating resources. A test secret cannot authenticate to the live service.
3. Read your roster, then link or invite
GET /v1/creators reads your existing CRD agency roster. Reuse its returned API creator IDs. Dashboard edits are reflected in API reads; PATCH /v1/creators/{id} updates that same creator’s name and merges aliases or official profiles.
Send POST /v1/creators with the assigned workspace_id, your stable external_id, display_name, and an existing crd_user_id belonging to your agency. Optional aliases and official profile URLs are connected to that creator’s CRD identity.
For a new creator, provide email, first_name, last_name, and optional locale instead of crd_user_id. This starts a real CRD invitation. The creator must accept and complete onboarding before scans are eligible. Store the returned creator ID and read GET /v1/creators/{id} for current enrollment and eligibility.
Choose the enrollment path that matches the creator
Optional aliases and supported profile_urls may be supplied on either enrollment path. A 201 response means the API enrollment was created, not that protection is active. email_sent: true means the email provider accepted the invitation message; it does not prove inbox delivery or acceptance. Invitations expire after seven days. An identical enrollment retry does not resend email; use the existing agency invitation controls for a resend. There is no public API resend endpoint.
| Creator situation | What your backend sends | What happens next |
|---|---|---|
| Already returned by the agency roster | Reuse its returned crt_… ID; use PATCH for identity edits. | Read eligibility and explicitly activate if needed. No invitation is required. |
| Existing CRD creator in this agency; assign your external ID | POST with workspace_id, external_id, display_name, and crd_user_id. | Links the existing profile; the current mandate and entitlement remain authoritative. |
| New creator being invited | POST with workspace_id, external_id, display_name, email, first_name, last_name, and optional locale. | CRD sends the existing invitation email. The creator accepts and completes the required portal steps before eligibility. |
4. Read eligibility, then explicitly activate
Read GET /v1/creators/{id} after enrollment. The creator needs an approved, signed, unexpired authorization and an active entitlement. The response reports authorization, entitlement, eligible, and protection_status. A newly created enrollment response is not proof that these checks passed.
When eligible is true, send a separate PUT /v1/creators/{id}/protection with {"status":"active"}. This is required even if the creator was already authorized. Accepting an invitation or approving a mandate does not automatically make that PUT request.
Activation enables the API scan schedule and starts its usage interval. It does not buy, cancel, or replace the native CRD subscription. A Read only key cannot activate; use Read and write.
A creator can be linked and eligible while protection_status is still not_activated. Your integration decides when to send the activation request after receiving or reading eligible: true.
Copyable enrollment and activation sequence
This Node.js example uses a Read and write key and the API origin supplied for your account. Replace every fictional identity before sending a live request. For an existing agency creator, use its crd_user_id instead of the email/name invitation fields, or reuse the ID returned by the roster.
The agency’s decision to enable protection belongs in your application. This example represents that saved choice with CRD_PROTECTION_REQUESTED. A successful invitation does not activate protection: the code makes a fresh eligibility read and sends the separate active PUT only when both the saved choice and current eligibility allow it.
After the initial enrollment, store its API creator ID. On creator.updated, refresh that ID and repeat only the eligibility/activation decision. This logic runs in your integration; CRD does not automatically activate a creator merely because its authorization was approved.
const baseUrl = process.env.CRD_BASE_URL;
const key = process.env.CRD_API_KEY;
if (!baseUrl || !key) throw new Error('Set CRD_BASE_URL and CRD_API_KEY');
async function crd(path, method = 'GET', body) {
const response = await fetch(`${baseUrl}${path}`, {
method,
headers: {
Authorization: `Bearer ${key}`,
...(body === undefined ? {} : { 'Content-Type': 'application/json' }),
},
...(body === undefined ? {} : { body: JSON.stringify(body) }),
});
const result = await response.json();
if (!response.ok) throw new Error(`${response.status}: ${result.error?.code}`);
return result;
}
const connection = await crd('/v1/me');
const enrollment = await crd('/v1/creators', 'POST', {
workspace_id: connection.workspaces[0].id,
external_id: 'your-stable-creator-001',
display_name: 'Example Creator',
email: '[email protected]',
first_name: 'Example',
last_name: 'Creator',
locale: 'en',
aliases: ['example_creator'],
profile_urls: ['https://onlyfans.com/example_creator'],
});
// Save enrollment.id and this original payload in your application.
// 201 creates enrollment; a matching repeat may return 200.
// The invitation email comes from enrollment, not activation.
const current = await crd(`/v1/creators/${enrollment.id}`);
// In production, read the agency's saved choice from YOUR application.
// This environment flag is only a runnable example of that saved intent.
const protectionRequested = process.env.CRD_PROTECTION_REQUESTED === 'true';
if (protectionRequested && current.eligible) {
const protection = await crd(`/v1/creators/${current.id}/protection`,
'PUT', { status: 'active' });
console.log({ creatorId: current.id, protection: protection.protection_status });
} else {
console.log({ creatorId: current.id, eligible: current.eligible,
action: protectionRequested ? 'await_creator_requirements' : 'await_agency_choice' });
}
// After creator.updated, run a fresh GET and the decision above again.
// Do not repeat enrollment just to check eligibility or resend an email.5. Request and follow a scan
Send POST /v1/creators/{id}/scans with an empty JSON body and an Idempotency-Key unique to this logical scan. Save the returned scan ID. A 202 response means queued; poll GET /v1/scans/{id} or receive a signed webhook.
After completion, read GET /v1/creators/{id}/cases. Results use that creator’s native CRD leak records, including existing and scheduled-scan findings. Native findings can have scan_id: null; API scan provenance is preserved when available. Register your HTTPS receiver in /agency/api before requesting the scan if you want its events delivered.
6. Track a deindex request
For an eligible case with verified API-scan provenance, POST /v1/cases/{id}/deindex-requests with provider: google and a stable Idempotency-Key. Store its ID and follow GET /v1/deindex-requests/{id}.
The API queues a real record for CRD processing. Native findings without verified API-scan provenance continue through the existing admin workflow; the API deindex route does not accept them. Acceptance does not mean Google received the request or removed a result. Display submitted_to_provider and removal_verified independently from the request status.
Request & response example
/v1/mecurl --request GET 'https://api.crd.systems/v1/me' \
--header "Authorization: Bearer $CRD_API_KEY"{
"partner": {
"id": "ptn_example",
"name": "Example Agency",
"kind": "agency"
},
"workspace_id": "wsp_example",
"key_id": "key_example",
"scopes": [
"workspaces:read",
"creators:read",
"creators:write"
],
"expires_at": null,
"environment": "live",
"workspaces": [
{
"id": "wsp_example",
"name": "Example Agency",
"external_id": "agency_001"
}
],
"engine_configured": true
}Illustrative response. Replace example resource IDs with the IDs returned by your API requests. View endpoint details.