API Reference
The DAMdaryl API lets you search your asset library, analyze social posts, and get brand-aware asset recommendations — programmatically.
Authentication
The v1 API uses two authentication methods depending on the endpoint:
For search, assets, and brands endpoints. Pass your API key as a Bearer token.
Authorization: Bearer dd_sk_your_api_keyAPI keys begin with dd_sk_
For Post Intelligence, Brand Context, and pairings endpoints. Use a Supabase session JWT or let cookies authenticate automatically.
Authorization: Bearer <supabase_jwt>In-browser requests authenticate via Supabase session cookie automatically.
Base URL & Versioning
All endpoints are versioned under /api/v1.
https://app.damdaryl.ai/api/v1All requests and responses use JSON, except for the Recommend Assets endpoint which streams Server-Sent Events.
Errors
Errors return a JSON body with an error field describing what went wrong.
| Status | Meaning |
|---|---|
| 400 | Bad request — missing or invalid field |
| 401 | Unauthorized — missing or invalid credentials |
| 403 | Forbidden — valid credentials but no access to this resource |
| 404 | Not found |
| 500 | Server error — safe to retry |
{ "error": "Missing required field: query" }Search Assets
🔑 API keyChoose assets for a natural language request using the same selector as the Library. Text semantic search is supported. Results retain factual limitations.
Request body
| Field | Type | Description | |
|---|---|---|---|
query | string | required | Natural language search query. |
brandId | string | optional | Restrict results to a specific brand. Defaults to your only brand if you have one. |
mode | "semantic" | "auto" | optional | Text search. Visual and hybrid modes are not supported. |
limit | number | optional | Upper bound, 1–100, default 20. Editorial selection returns at most six useful choices; source lookup can return more. Results are never padded. |
offset | number | optional | Offset within the delivered selection, default 0. The total counts this selection, not every library asset. |
filters | object | optional | Optional structured filters, preserved during alternative recall. Cannot be combined with exact filename/folder lookup; use visual constraints in the query instead. |
Example request
POST /api/v1/search
Authorization: Bearer dd_sk_your_api_key
Content-Type: application/json
{
"query": "warm lifestyle photography with natural light",
"brandId": "brand_abc123",
"mode": "semantic",
"limit": 10
}Response
{
"results": [
{
"assetId": "asset_xyz789",
"imageUrl": "https://...",
"thumbnailUrl": "https://...",
"fileName": "summer-campaign-01.jpg",
"oneLiner": "Warm morning light filtering through linen curtains",
"score": null,
"evidence": "unverified",
"unconfirmed": [],
"unconfirmedExclusions": [],
"fileType": "image/jpeg"
}
],
"total": 1,
"query": {
"original": "warm lifestyle photography with natural light",
"processed": "warm lifestyle photography with natural light"
},
"timing": {
"total": 12000
}
}List Brands
🔑 API keyReturns all brands your API key has access to, along with their analyzed asset counts.
Response
{
"brands": [
{
"id": "brand_abc123",
"name": "Acme Co",
"industry": "consumer_goods",
"logoUrl": "https://...",
"assetCount": 1204
}
]
}Get Asset
🔑 API keyReturns full metadata for a single asset, including AI analysis, brand intelligence, and platform fit scores. Suitability scores are modeled estimates, not measured campaign performance.
Path parameters
| Parameter | Type | Description | |
|---|---|---|---|
assetId | string | required | The asset ID returned from search results. |
Response
{
"asset": {
"id": "asset_xyz789",
"brandId": "brand_abc123",
"fileName": "summer-campaign-01.jpg",
"fileUrl": "https://...",
"thumbnailUrl": "https://...",
"fileType": "image/jpeg",
"status": "analyzed",
"createdAt": "2026-01-15T10:23:00Z",
"analyzedAt": "2026-01-15T10:23:45Z",
"width": 3000,
"height": 2000,
"fileSizeBytes": 2457600,
"oneLiner": "Warm morning light filtering through linen curtains",
"altText": "Person reading by a sunlit window",
"hasProduct": false,
"hasPeople": true,
"hasText": false,
"peopleCount": 1,
"mood": "calm",
"energy": "low",
"productionStyle": "lifestyle",
"contentType": "editorial",
"setting": "interior",
"season": "summer",
"brandArchetype": "Caregiver",
"brandPersonality": { "sincerity": 0.9, "excitement": 0.2, ... },
"audienceArchetypes": ["The Explorer", "The Everyman"],
"aspirationTension": "connection_vs_independence",
"emotionalRegister": "tender",
"platformScores": { "instagram": 0.91, "pinterest": 0.85, "linkedin": 0.34 },
"platformNativeFit": "instagram",
"postTypeFit": ["organic_lifestyle", "story"],
"contentPillarFit": ["brand_story", "product_in_life"],
"funnelStageFit": "awareness",
"qualityScore": 0.87,
"scrollStopScore": 0.74,
"authenticityScore": 0.92,
"aestheticTags": ["soft", "warm", "airy"],
"aestheticEra": "contemporary",
"colorTemperature": "warm",
"dominantColors": ["#F5DEB3", "#D2B48C"],
"copySuggestion": "Some mornings are worth savoring.",
"captionToneCompatibility": "tender_and_aspirational",
"hasV5Analysis": true
}
}Fields under brand intelligence, platform fit, and aesthetic are only populated when hasV5Analysis is true.
Get Download URL
🔑 API keyReturns a download URL for the original or thumbnail variant of an asset. Use this endpoint instead of storing raw file URLs — the URL format may change when signed URLs are introduced.
Query parameters
| Parameter | Type | Description | |
|---|---|---|---|
variant | "original" | "thumbnail" | optional | Which variant to fetch. Defaults to "original". |
Response
{
"url": "https://...",
"fileName": "summer-campaign-01.jpg",
"contentType": "image/jpeg",
"variant": "original",
"expiresAt": null
}expiresAt is currently null. URLs are public and do not expire today, but this field will be populated when signed URLs with expiration are introduced.
Analyze Post
🔐 SessionAnalyzes a social media post to extract intent, tone, emotional register, and visual requirements. The resulting PostAnalysis object is required input for Recommend Assets.
Request body
| Field | Type | Description | |
|---|---|---|---|
postText | string | required | The social media post copy to analyze. |
brandId | string | required | The Brand Context for the analysis. Enables brand-aware scoring when generated context exists. |
platformContext | string | optional | Target platform (e.g. "instagram", "linkedin"). Improves platform-specific scoring. |
Example request
POST /api/v1/post-intelligence/analyze
Authorization: Bearer <supabase_jwt>
Content-Type: application/json
{
"postText": "Some mornings are worth savoring. ☕",
"brandId": "brand_abc123",
"platformContext": "instagram"
}Response
{
"primary_intent": "brand_awareness",
"emotional_register": "tender",
"tonal_register": ["warm", "aspirational"],
"visual_implication": "soft morning light, cozy interior, slow moment",
"brand_archetype": "Caregiver",
"target_audience_archetypes": ["The Everyman", "The Explorer"],
"platform_context": "instagram",
"post_type": "organic_lifestyle"
}Recommend Assets
🔐 SessionRuns the full recommendation pipeline against your asset library and streams results as Server-Sent Events. Requires the original user-written text in postAnalysis.raw_post_text. Derived fields cannot replace the brief. A generated lens profile is not required.
text/event-stream (SSE), not JSON. Each event is a JSON object prefixed with data: .Request body
| Field | Type | Description | |
|---|---|---|---|
postAnalysis | PostAnalysis | required | Analysis containing the complete original wording in raw_post_text. Image choice uses that original text. |
brandId | string | required | Brand to search within; caller access and plan limits are checked. |
SSE stream events
Progress events are emitted throughout the pipeline. Each is a data: {...} line.
// Progress events from the shared selector
data: {"step":"context","status":"in-progress","message":"Loading brand context..."}
data: {"step":"analyzing","status":"in-progress","message":"Understanding your query..."}
data: {"step":"daryl","status":"in-progress","message":"Curating recommendations..."}
// Final event — one of:
data: {"type":"results","matchState":"consider","recommendations":[...]}
data: {"type":"no-match","gapDiagnosis":"...","closest":[]}
data: {"type":"error","message":"Asset selection could not finish. Please try again."}}Recommendation object
{
"assetId": "asset_xyz789",
"fileName": "summer-campaign-01.jpg",
"thumbnailUrl": "https://...",
"rationale": "Two empty chairs face the ocean.",
"selectionMethod": "brief_fit",
"evidence": "related",
"unconfirmed": ["exact product identity"],
"unconfirmedExclusions": [],
"lensScores": null,
"lensMatchQuality": null,
"compositeScore": null
}}New recommendations use the shared brief comparison. Legacy numeric lens fields are null when unmeasured. A completed empty choice is separate from a provider error; preserve every image’s evidence and restriction warnings.
Get Brand Context
🔐 SessionReturns the generated profile data that powers Brand Context. The API keeps the existing profile field for compatibility. It returns profile: null with coldStart: true when Brand Context has not yet been generated.
Response — profile exists
{
"profile": {
"brand_id": "brand_abc123",
"primary_archetype": "Caregiver",
"secondary_archetype": "Creator",
"brand_personality": {
"sincerity": 0.85,
"excitement": 0.3,
"competence": 0.6,
"sophistication": 0.45,
"ruggedness": 0.1
},
"content_pillars": ["brand_story", "product_in_life", "community"],
"audience_archetypes": ["The Everyman", "The Explorer"],
"platform_focus": ["instagram", "pinterest"],
"production_style_preference": "lifestyle",
"brand_profile_generated_at": "2026-03-01T12:00:00Z",
"brand_profile_version": 2
}
}Response — cold start (profile not yet generated)
{ "profile": null, "coldStart": true }Generate Brand Context
🔐 SessionTriggers background analysis of the brand's analyzed assets to generate the profile data used by Brand Context. Returns immediately — poll Get Brand Context until coldStart is false.
Response
{ "status": "triggered", "brandId": "brand_abc123" }Save Pairing
🔐 SessionSaves a confirmed post → asset pairing to your history. Use this after a user accepts a recommendation so it can be referenced and analyzed later.
Request body
| Field | Type | Description | |
|---|---|---|---|
brandId | string | required | Brand this pairing belongs to. |
postText | string | required | The post copy. |
assetId | string | required | The chosen asset ID. |
rationale | string | required | Why this asset was chosen (from the recommendation). |
lensScores | object | null | required | Historical lens scores, or null when no numeric assessment was made. |
compositeScore | number | null | required | Historical numeric fit score (0–1), or null when unmeasured. |
postAnalysis | PostAnalysis | required | The full PostAnalysis object from Analyze Post. |
platform | string | optional | Target platform for the post. |
assetFileName | string | optional | Asset file name for display purposes. |
assetThumbnailUrl | string | optional | Thumbnail URL for display purposes. |
Response
{ "id": "pairing_abc", "created_at": "2026-03-29T14:22:00Z" }List Pairings
🔐 SessionReturns the 50 most recent post → asset pairings for a brand, ordered newest first.
Query parameters
| Parameter | Type | Description | |
|---|---|---|---|
brandId | string | required | Filter pairings by brand. |
Response
{
"pairings": [
{
"id": "pairing_abc",
"brand_id": "brand_abc123",
"post_text": "Some mornings are worth savoring. ☕",
"platform": "instagram",
"asset_id": "asset_xyz789",
"asset_file_name": "summer-campaign-01.jpg",
"asset_thumbnail_url": "https://...",
"rationale": "Strong mood alignment...",
"lens_scores": { "brand": 0.88, "audience": 0.79, "mood": 0.95, "platform": 0.84 },
"composite_score": 0.87,
"post_analysis": { ... },
"created_at": "2026-03-29T14:22:00Z"
}
]
}