Step 1: Create a revenue source in CRM Solid
Budget: 1 minute. In CRM Solid, go to Finance > Revenue sources > New source. Give the source a recognizable name - "WooCommerce store", "Shopify UK", or "Custom PHP site" all work. The name appears on every ledger entry that comes from this source, so pick something you will recognize at a glance six months later.
Choose Push or Pull mode. You can add a second source of the other type later if you want both. Save. CRM Solid creates an isolated record and you land on the source detail page.
Step 2: Generate an rsk_ API key (push mode)
Budget: 1 minute. In the source detail screen, open the API key tab and click Generate key. The panel shows the raw key once, in the format rsk_live_xxxxxxxxxxxxxxxx. Copy it now.
Store the key as an environment variable on your server (e.g.,CRM_SOLID_RSK_KEY). Never commit it to source control. CRM Solid stores only a BCrypt hash of the key - it cannot be retrieved from the panel again. If you lose it, generate a new one; old keys can be revoked from the same tab.
Security note: the rsk_ key is scoped to revenue event ingestion only. A leaked key cannot read contacts, send messages, or access any other part of the API.
Step 3: POST your first sale event
Budget: 5 minutes. From your site backend, send a POST request to https://api.crmsolid.com/public/v1/revenue/events with a JSON body. Below is a realistic example using curl:
curl -X POST https://api.crmsolid.com/public/v1/revenue/events \
-H "Authorization: Bearer rsk_live_YOUR_KEY_HERE" \
-H "Content-Type: application/json" \
-d '{
"externalId": "wc_10482",
"type": "income",
"status": "completed",
"amount": 129.00,
"currency": "USD",
"occurredAt": "2026-06-04T14:32:18Z",
"tax": 21.50,
"fee": 4.07,
"product": "Pro Plan",
"sku": "PRO-001",
"quantity": 1,
"unitPrice": 129,
"discount": 0,
"coupon": "SPRING",
"customer": {
"externalId": "cust_77",
"email": "[email protected]",
"name": "Sara Miller",
"country": "US"
},
"metadata": { "orderTags": ["vip"] }
}'Only three fields are required: externalId, amount, and occurredAt. Every other field is optional. Send as much as you have:
externalId- your own order or transaction ID. This is the deduplication key (see Step 4).type-"income"(default) or"expense"to push costs like ad spend.status-"completed","pending","refunded", or"failed".taxandfee- tax collected and payment processing fee. CRM Solid computes net as amount minus fee automatically.customer.email- enables automatic CRM contact matching (see Step 6).
A successful response returns HTTP 200 with the created or updated ledger entry ID.
Step 4: Verify idempotency with a duplicate post
Budget: 2 minutes. POST the exact same payload again (same externalId). Open Finance > Revenue sources > (your source) and confirm the event count is still 1, not 2. Then change the amount or status and post again; the existing entry should update, not duplicate.
This behavior is intentional and permanent. Re-posting the sameexternalId is the correct way to update a sale - for example, marking a pending order as completed, or applying a refund. It also means your site can retry failed HTTP requests without risk of double-counting revenue.
Step 5: Configure pull mode
If you chose pull mode, the source detail screen shows a pull settings tab instead of an API key tab. Fill in:
- Feed URL: the endpoint on your site that returns sale events. CRM Solid will GET this URL on the polling interval. The response must be JSON with an
eventsarray and optionally acursorfield for pagination. - Polling interval: how often CRM Solid fetches. Choices range from every 5 minutes to every 1440 minutes (once per day). Near-real-time reporting needs 5-15 minutes; daily batch reporting is fine at 60+ minutes.
- Authentication: none, a bearer token, or a custom HTTP header name and value. Credentials are stored encrypted.
A minimal feed response looks like this:
{
"cursor": "cur_88240",
"events": [
{ "externalId": "ord_5521", "amount": 240, "occurredAt": "2026-06-04T10:00:00Z" },
{ "externalId": "ord_5522", "amount": 59, "occurredAt": "2026-06-04T11:30:00Z" }
]
}Each run resumes from the last cursor, so CRM Solid only ever fetches new events. Click Test connection to preview the first page of events without ingesting anything. Confirm the shape looks correct, then click Enable pull.
Step 6: Check automatic customer matching
Budget: 1 minute. After your first accepted event, go to Contacts and search for the email address you sent in customer.email.
- If a contact with that email already existed, open their record. The activity timeline shows the revenue event linked to your source, including the product name and amount.
- If no contact existed, CRM Solid created one using the name, email, phone, company, country, and city fields from the customer object. You can enrich that contact further from the panel.
Every subsequent sale from the same email address accumulates in the same contact record, giving your sales team a full purchase history alongside the conversation history from other channels.
Step 7: Review synced sales in the Finance ledger
Open Finance > Ledger and filter by your source name. Each row shows the externalId, amount, currency, product, status, and the linked contact. You can click any entry to see the full event payload.
To push costs alongside income, send expense events:
{
"externalId": "adspend_2026_06_04",
"type": "expense",
"amount": 55.00,
"currency": "USD",
"occurredAt": "2026-06-04T00:00:00Z",
"product": "Google Ads - June 4"
}Expense events appear in the same ledger with a negative sign. The profit/loss view in the next step uses both income and expense events together.
Step 8: Monitor per-source profit and loss
Open Finance > Revenue sources > (your source) > Profit and loss. The daily bar chart shows net profit (income minus fees and expense events) per day. Days where costs exceeded income show as negative bars.
Below the chart, the sync log summary for the most recent run shows four counts:
- Accepted - new events written to the ledger.
- Duplicates - externalIds already in the ledger; no action taken.
- Updated - externalIds re-sent with changed fields; existing entry updated.
- Rejected - events that failed schema validation; click to see the error reason.
If Rejected is consistently above zero, inspect the error reason. Common causes: missing required field, a non-ISOoccurredAt date format, or an unknown currency code.