Developer guideTurn a domain into account data
Quote a single company lookup, approve execution, and read the resulting company record and receipt through a small explicit API flow.
Single-company lookup. No batch contacts, verified revenue or buying-intent enrichment.
01Define the entity
Send company-research with input.domain to /api/tools/quote. Use the company’s actual business domain, not a person’s email or an unrelated product keyword. Review the normalized input. A valid domain does not guarantee that the source has a matching record.
02Review the quote
A quoted response includes quote_id, expires_at and user_price_usd. The sample client prints those review fields without printing the private receipt capability. Keep that capability until execution and recovery are complete. Request availability and daily limits remain separate from syntax validation.
03Approve one lookup
The run endpoint requires approved:true and the quote identity. A guest supplies access_token; authenticated ownership follows the account. Reusing the same quote can return its saved result. Do not treat a timeout or indeterminate response as permission to create a duplicate lookup.
04Check the result identity
On completed status, inspect data and the company brief. Match the domain and name before copying facts to your CRM. Treat employee ranges as estimates. Absent fields remain absent: the API does not verify personal contacts, buying intent or revenue.
05Retain provenance and errors
Save the retrieval timestamp with the output. It records when the response was captured, not the last time every source field changed. Preserve status and error.code when no record is delivered. The example displays data only after checking the protocol response.
06Choose a downstream format
Keep JSON for the original structure and a readable brief for account review. A manual CSV handoff can populate a Clay table. Neither format creates batch processing, freshness monitoring or a supported native integration by itself; test those workflows separately.
Keep these fields
- input.domain
- One public company domain
- data
- Returned company record on success
- brief
- Readable presentation with missing-field context
- provenance.retrieved_at
- Response capture time, not field freshness
// Node.js: save as example.mjs. No API key is invented here.
const base = 'https://thespawn.io';
async function request(path, options = {}) {
const response = await fetch(base + path, options);
const body = await response.json();
if (!response.ok) throw new Error(body.error?.code || `HTTP ${response.status}`);
return body;
}
const quote = await request('/api/tools/quote', {
method: 'POST', headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({"tool": "company-research", "input": {"domain": "stripe.com"}})
});
if (quote.status !== 'quoted') throw new Error('No executable quote');
console.log({ input: quote.input, expires_at: quote.expires_at,
user_price_usd: quote.user_price_usd });
// Review the input and price above. Set APPROVE_TOOL_RUN=yes to execute.
if (process.env.APPROVE_TOOL_RUN !== 'yes') process.exit(0);
const receipt = await request('/api/tools/run', {
method: 'POST', headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ quote_id: quote.quote_id,
access_token: quote.access_token, approved: true })
});
console.log({ status: receipt.status, data: receipt.data });
// Read the SAME request again; this does not create a new quote.
const saved = await request('/api/tools/requests/' + quote.quote_id, {
headers: { 'X-Tool-Access-Token': quote.access_token }
});
console.log({ status: saved.status, data: saved.data });
// Keep quote.access_token private. Never put it in a URL or shared logs.Sources and next steps
Reviewed 2026-09-14