Duda Sites Service
๐ Overviewโ
Service Path: external/Integrations/Duda/services/sites.service.js
The Duda Sites service provides comprehensive site management for the Duda website builder platform. Core responsibilities include:
- Site Management: Create, read, update, and delete Duda websites
- Publishing: Publish and unpublish sites
- Analytics: Retrieve site analytics data
- Form Data: Access form submissions from Duda sites
- Templates: Get template information
- Plan Management: Set site subscription plans
๐๏ธ Collections Usedโ
- Stores Duda API username and password for authentication
๐ Data Flowโ
Site Creation Flowโ
sequenceDiagram
participant Client
participant SitesService
participant ConfigDB
participant DudaAPI
Client->>SitesService: createSite(dudaHeader, body, account_id)
SitesService->>ConfigDB: Find API credentials
ConfigDB-->>SitesService: Duda credentials
alt Credentials Not Found
SitesService-->>Client: 404 - Api token not found
else Credentials Found
SitesService->>SitesService: Generate Basic Auth token
SitesService->>SitesService: Add external_uid to site_data
SitesService->>DudaAPI: POST /sites/multiscreen/create
Note over SitesService,DudaAPI: Authorization: Basic {token}
DudaAPI-->>SitesService: Site created response
SitesService-->>Client: Site data
end
style SitesService fill:#e3f2fd
style DudaAPI fill:#fff4e6
Site Publishing Flowโ
flowchart TD
A[Publish Request] --> B[Fetch Duda credentials]
B --> C{Credentials exist?}
C -->|No| D[Throw 404 error]
C -->|Yes| E[Generate Basic Auth token]
E --> F[POST to Duda API /publish/:id]
F --> G{Publish successful?}
G -->|Yes| H[Return true]
G -->|No| I[Throw error]
D --> J[End]
H --> J
I --> J
style F fill:#e8f5e9
style H fill:#c8e6c9
๐ง Business Logic & Functionsโ
getSites(dudaHeader, account_id)โ
Purpose: Retrieve all Duda sites for a specific account by external ID
Parameters:
dudaHeader(ObjectId) - Config ID containing Duda API credentialsaccount_id(ObjectId) - DashClicks account ID (used as external_uid in Duda)
Returns:
// Array of site objects
[
{
site_name: String,
site_domain: String,
site_business_info: Object,
site_seo: Object,
site_data: {
external_uid: String, // DashClicks account_id
},
// ... other Duda site fields
},
];
Business Logic Flow:
-
Fetch API Credentials
let query = await Config.findById(dudaHeader);
if (!query) {
throw notFound('Api token not found');
} -
Generate Authentication Token
const access_token = utils.base64(query);
// Encodes username:password to Base64 -
Call Duda API
let options = {
method: 'GET',
url: `${DUDA_SITES_DOMAIN}/byexternalid/${account_id}`,
headers: {
'Content-Type': 'application/json',
authorization: `Basic ${access_token}`,
},
};
const response = await axios(options);
return response.data;
API Endpoint: GET https://api.duda.co/api/sites/multiscreen/byexternalid/:account_id
Key Business Rules:
- External UID: Uses DashClicks account_id as Duda's external_uid for tracking
- Basic Auth: Requires Base64-encoded username:password from Config
- Account Isolation: Returns only sites belonging to specified account
getSite(dudaHeader, id)โ
Purpose: Retrieve a single Duda site by site ID
Parameters:
dudaHeader(ObjectId) - Config ID for Duda credentialsid(String) - Duda site ID
Returns:
{
site_name: String,
site_domain: String,
site_default_domain: String,
site_business_info: {
business_name: String,
address: Object,
phone: String,
email: String
},
site_seo: {
og_image: String,
title: String,
description: String
},
site_data: {
external_uid: String
},
creation_date: String,
last_published_date: String,
last_reset_by: String,
account_name: String,
// ... more Duda fields
}
Business Logic Flow:
- Fetch credentials from Config
- Generate Basic Auth token
- Call Duda API:
GET /sites/multiscreen/:id - Return site details
API Endpoint: GET https://api.duda.co/api/sites/multiscreen/:id
Key Business Rules:
- Direct Access: No account filtering, requires exact site ID
- Detailed Data: Returns complete site configuration including SEO and business info
createSite(dudaHeader, body, account_id)โ
Purpose: Create a new Duda website
Parameters:
dudaHeader(ObjectId) - Config ID for credentialsbody(Object) - Site creation dataaccount_id(ObjectId) - DashClicks account ID
Request Body Structure:
{
template_id: String, // Optional: Duda template to use
site_data: {
site_domain: String, // Custom domain
site_business_info: Object, // Business information
site_seo: Object, // SEO settings
// ... other site configuration
},
default_domain_prefix: String // Subdomain for default Duda domain
}
Returns:
{
site_name: String, // Generated site ID
site_domain: String,
site_default_domain: String,
// ... complete site data
}
Business Logic Flow:
-
Validate Request Body
if (Object.keys(body).length <= 0) {
throw badRequest('Invalid request body.');
} -
Inject External UID
body.site_data = {
...(body.site_data ?? {}),
external_uid: account_id, // Track ownership
}; -
Create Site via Duda API
const options = {
method: 'POST',
url: `${DUDA_SITES_DOMAIN}/create`,
headers: {
'content-type': 'application/json',
authorization: `Basic ${access_token}`,
},
data: body,
};
const response = await axios(options);
return response.data;
API Endpoint: POST https://api.duda.co/api/sites/multiscreen/create
Key Business Rules:
- External UID Injection: Automatically adds DashClicks account_id to site_data
- Template Support: Can create from template or blank site
- Default Domain: Duda generates default subdomain (e.g., sitename.multiscreensite.com)
deleteSites(dudaHeader, id)โ
Purpose: Delete a Duda site permanently
Parameters:
dudaHeader(ObjectId) - Config IDid(String) - Duda site ID to delete
Returns: true on success
Business Logic Flow:
- Fetch credentials
- Generate auth token
- Call DELETE endpoint
- Return success boolean
API Endpoint: DELETE https://api.duda.co/api/sites/multiscreen/:id
Key Business Rules:
- Permanent Deletion: Cannot be undone
- No Validation: Doesn't check if site belongs to account (relies on Duda API permissions)
updateSites(dudaHeader, id, requestBody)โ
Purpose: Update an existing Duda site configuration
Parameters:
dudaHeader(ObjectId) - Config IDid(String) - Site ID to updaterequestBody(Object) - Updated site data
Request Body Example:
{
site_data: {
site_domain: 'newdomain.com',
site_business_info: {
business_name: 'Updated Business Name',
phone: '555-0100'
}
}
}
Returns: true on success
Business Logic Flow:
-
Validate Request Body
if (Object.keys(requestBody).length <= 0) {
throw badRequest('Request body required.');
} -
Update via Duda API
var options = {
method: 'POST',
url: `${DUDA_SITES_DOMAIN}/update/${id}`,
headers: {
'Content-Type': 'application/json',
authorization: `Basic ${access_token}`,
},
data: requestBody,
};
await axios(options);
return true;
API Endpoint: POST https://api.duda.co/api/sites/multiscreen/update/:id
Key Business Rules:
- Partial Updates: Only provided fields are updated
- POST Method: Uses POST instead of PUT/PATCH
publishSites(dudaHeader, id)โ
Purpose: Publish a Duda site to make it live
Parameters:
dudaHeader(ObjectId) - Config IDid(String) - Site ID to publish
Returns: true on success
Business Logic Flow:
let options = {
method: 'POST',
url: `${DUDA_SITES_DOMAIN}/publish/${id}`,
headers: {
'Content-Type': 'application/json',
authorization: `Basic ${access_token}`,
},
};
await axios(options);
return true;
API Endpoint: POST https://api.duda.co/api/sites/multiscreen/publish/:id
Key Business Rules:
- Live Deployment: Makes site publicly accessible
- No Confirmation: Immediate publish without preview
- Updates Last Published Date: Duda tracks publish timestamp
unpublishSites(dudaHeader, id)โ
Purpose: Unpublish a Duda site to take it offline
Parameters:
dudaHeader(ObjectId) - Config IDid(String) - Site ID to unpublish
Returns: true on success
API Endpoint: POST https://api.duda.co/api/sites/multiscreen/unpublish/:id
Key Business Rules:
- Takes Site Offline: Site becomes inaccessible to public
- Preserves Data: Content is not deleted, can be republished
getTemplate(dudaHeader, builderID)โ
Purpose: Get Duda template information by template ID
Parameters:
dudaHeader(ObjectId) - Config IDbuilderID(String) - Duda template ID
Returns:
{
template_id: String,
template_name: String,
preview_url: String,
thumbnail_url: String,
desktop_thumbnail_url: String,
tablet_thumbnail_url: String,
mobile_thumbnail_url: String
}
API Endpoint: GET https://api.duda.co/api/sites/multiscreen/templates/:builderID
Key Business Rules:
- Template Info Only: Returns metadata, not template content
- Used for Template Selection: Helps users choose templates before site creation
getAnalytics(dudaHeader, params, builder_id)โ
Purpose: Retrieve site analytics data for a date range
Parameters:
dudaHeader(ObjectId) - Config IDparams(Object) - Query parametersfrom(String) - Start date (YYYY-MM-DD)to(String) - End date (YYYY-MM-DD)dimension(String) - Analytics dimension (e.g., 'system', 'geo')
builder_id(String) - Duda site ID
Returns:
{
site_name: String,
date_range: {
from: String,
to: String
},
activities: [
{
dimension: String,
data: [
{
activity: String,
value: Number
}
]
}
]
}
Business Logic Flow:
let options = {
method: 'GET',
url: `https://api.duda.co/api/analytics/site/${builder_id}`,
headers: {
'Content-Type': 'application/json',
authorization: `Basic ${access_token}`,
},
params, // Includes from, to, dimension
};
const response = await axios(options);
return response.data;
API Endpoint: GET https://api.duda.co/api/analytics/site/:builder_id
Key Business Rules:
- Date Range Required: Must specify from and to dates
- Dimension Support: Can segment by system, geo, etc.
- Activity Metrics: Returns pageviews, unique visits, etc.
getFormData(dudaHeader, params, id)โ
Purpose: Retrieve form submissions from a Duda site
Parameters:
dudaHeader(ObjectId) - Config IDparams(Object) - Query parametersfrom(String) - Start dateto(String) - End date
id(String) - Duda site ID
Returns:
[
{
form_title: String,
message: Object, // Form field values
date: String,
ip: String,
},
];
API Endpoint: GET https://api.duda.co/api/sites/multiscreen/get-forms/:id
Key Business Rules:
- Date Filtering: Returns submissions within date range
- All Forms: Includes all forms from the site
- Field Data: message object contains all form fields
setSitesPlan(dudaHeader, id, plan)โ
Purpose: Set or update the subscription plan for a Duda site
Parameters:
dudaHeader(ObjectId) - Config IDid(String) - Duda site IDplan(String) - Plan identifier (e.g., 'BASIC', 'PREMIUM')
Returns: true on success
API Endpoint: POST https://api.duda.co/api/sites/multiscreen/:id/plan/:plan
Key Business Rules:
- Plan Management: Controls site features based on plan
- Billing Impact: May affect Duda billing
๐ Integration Pointsโ
Website Creation Workflowโ
const sitesService = require('./services/sites.service');
// Step 1: Create site from template
const site = await sitesService.createSite(
dudaConfigId,
{
template_id: 'template_123',
default_domain_prefix: 'mybusiness',
site_data: {
site_business_info: {
business_name: 'My Business',
phone: '555-0100',
},
},
},
accountId,
);
// Step 2: Publish site
await sitesService.publishSites(dudaConfigId, site.site_name);
// Step 3: Set plan
await sitesService.setSitesPlan(dudaConfigId, site.site_name, 'PREMIUM');
Analytics Dashboardโ
// Get analytics for last 30 days
const analytics = await sitesService.getAnalytics(
dudaConfigId,
{
from: '2025-09-09',
to: '2025-10-09',
dimension: 'system',
},
siteId,
);
console.log(
`Pageviews: ${analytics.activities[0].data.find(d => d.activity === 'pageviews').value}`,
);
Form Submission Integrationโ
// Get form submissions
const forms = await sitesService.getFormData(
dudaConfigId,
{
from: '2025-10-01',
to: '2025-10-09',
},
siteId,
);
// Process form submissions
forms.forEach(form => {
console.log(`Form: ${form.form_title}`);
console.log(`Submitted: ${form.date}`);
console.log(`Data:`, form.message);
});
๐งช Edge Cases & Special Handlingโ
Missing API Credentialsโ
All Functions Check for Credentials:
let query = await Config.findById(dudaHeader);
if (!query) {
throw notFound('Api token not found');
}
Error: 404 - Api token not found
Empty Request Bodyโ
createSite and updateSites Validation:
if (Object.keys(body).length <= 0) {
throw badRequest('Invalid request body.');
}
Error: 400 - Invalid request body
External UID Injectionโ
Automatic Account Tracking:
body.site_data = {
...(body.site_data ?? {}),
external_uid: account_id, // Always injected
};
Duda API Errorsโ
Axios Error Handling:
try {
const response = await axios(options);
return response.data;
} catch (error) {
// Axios automatically throws for 4xx/5xx responses
// Error includes response data from Duda
throw error;
}
โ ๏ธ Important Notesโ
-
Authentication: All functions require valid Duda API credentials stored in Config collection with Base64 encoding.
-
External UID: DashClicks account_id automatically added to site_data.external_uid for ownership tracking.
-
Error Handling: Uses custom error utilities (
notFound,badRequest) for consistent error responses. -
Environment Variables:
DUDA_SITES_DOMAINmust be set (typicallyhttps://api.duda.co/api/sites/multiscreen). -
Basic Auth: Duda API uses Basic Authentication with username:password Base64-encoded.
-
No Async Validation: Functions don't validate site ownership before operations (relies on Duda API permissions).
-
Permanent Operations: Delete and unpublish operations cannot be undone.
-
Form Data Limitations: Form retrieval limited by Duda API date range restrictions.
-
Analytics Dimensions: Supported dimensions depend on Duda API capabilities (system, geo, etc.).
-
Plan Management: Plan changes may have billing implications on Duda side.
-
Template Access: Template retrieval requires template ID, used primarily during site creation.
-
Publishing: Sites must be published to be publicly accessible; updates to unpublished sites are not visible.
๐ Related Documentationโ
- Duda Accounts Service - Duda account management
- Duda Content Service - Site content management
- Duda Webhook Service - Webhook handling
- Duda API Documentation - Official Duda API docs