Skip to main content

๐ŸŽฏ Duda Accounts Service

๐Ÿ“– Overviewโ€‹

The Duda Accounts service manages Duda sub-accounts for client access, SSO (Single Sign-On) for editor access, and site permissions. This enables agencies to provide clients with direct access to the Duda website editor.

Source File: external/Integrations/Duda/services/accounts.service.js
External API: Duda Accounts API
Primary Use: Client onboarding and editor access

๐Ÿ—„๏ธ Collections Usedโ€‹

configโ€‹

  • Operations: Read
  • Model: external/models/config.js
  • Usage Context: Fetch Duda API credentials for authentication

๐Ÿ”„ Data Flowโ€‹

flowchart TD
A[Create Account Request] --> B[Fetch API Credentials]
B --> C[Generate Base64 Token]
C --> D[POST to Duda API]
D --> E{Success?}
E -->|Yes| F[Return true]
E -->|No| G[Throw Error]

style D fill:#e8f5e9
style F fill:#c8e6c9

๐Ÿ”ง Business Logic & Functionsโ€‹

createAccount(dudaHeader, accountName)โ€‹

Purpose: Create a Duda sub-account (CUSTOMER type) for client access

Source: services/accounts.service.js

External API Endpoint: POST https://api.duda.co/api/accounts/create

Parameters:

  • dudaHeader (ObjectId) - Config ID containing Duda API credentials
  • accountName (String) - Unique account name for the client

Returns: Promise<Boolean> - Returns true on success

Business Logic Flow:

  1. Fetch API Credentials

    let query = await Config.findById(dudaHeader);
    if (!query) {
    throw notFound('Api token not found');
    }
  2. Validate Account Name

    if (!accountName) {
    throw badRequest('Account name is required.');
    }
  3. Build Request

    const access_token = utils.base64(query);

    const dataToSend = {
    account_name: accountName,
    account_type: 'CUSTOMER', // Always CUSTOMER type
    };
  4. Call Duda API

    const options = {
    method: 'POST',
    url: 'https://api.duda.co/api/accounts/create',
    headers: {
    'content-type': 'application/json',
    authorization: `Basic ${access_token}`,
    },
    data: dataToSend,
    };
    await axios(options);

    return true;

API Request Example:

POST https://api.duda.co/api/accounts/create
Authorization: Basic {base64_token}
Content-Type: application/json

{
"account_name": "client-company-123",
"account_type": "CUSTOMER"
}

Error Handling:

  • 400 Bad Request: Missing account name
  • 404 Not Found: API credentials not found
  • 409 Conflict: Account name already exists (from Duda)

Example Usage:

await accountsService.createAccount(dudaConfigId, 'acme-corp');
// Creates Duda account for Acme Corp client

Key Business Rules:

  • Account Type: Always creates CUSTOMER type accounts (not STAFF or AGENCY)
  • Unique Names: Account names must be unique across Duda platform
  • No Return Data: Duda doesn't return account details, just success/failure

grantSiteAccess(dudaHeader, { siteName, accountName, permissions })โ€‹

Purpose: Grant a Duda account access to a specific site with defined permissions

Source: services/accounts.service.js

External API Endpoint: POST https://api.duda.co/api/accounts/:accountName/sites/:siteName/permissions

Parameters:

  • dudaHeader (ObjectId) - Config ID
  • siteName (String) - Duda site ID
  • accountName (String) - Duda account name
  • permissions (String[]) - Permission levels
    • Options: ['EDIT'], ['PUBLISH'], ['EDIT', 'PUBLISH'], etc.

Returns: Promise<Boolean> - Returns true on success

Business Logic Flow:

  1. Fetch Credentials

    let query = await Config.findById(dudaHeader);
    if (!query) {
    throw notFound('Api token not found');
    }
  2. Validate Permissions

    if (!permissions) {
    throw badRequest('Invalid request body.');
    }
  3. Grant Access

    const access_token = utils.base64(query);

    const dataToSend = {
    permissions,
    };

    const options = {
    method: 'POST',
    url: `https://api.duda.co/api/accounts/${accountName}/sites/${siteName}/permissions`,
    headers: {
    'content-type': 'application/json',
    authorization: `Basic ${access_token}`,
    },
    data: dataToSend,
    };
    await axios(options);

    return true;

API Request Example:

POST https://api.duda.co/api/accounts/acme-corp/sites/site_abc123/permissions
Authorization: Basic {base64_token}
Content-Type: application/json

{
"permissions": ["EDIT", "PUBLISH"]
}

Permission Levels:

  • EDIT: Can edit site content and design
  • PUBLISH: Can publish/unpublish site
  • STATS_TAB: Can view analytics
  • DEV_MODE: Can access developer mode
  • REPUBLISH: Can republish site

Example Usage:

await accountsService.grantSiteAccess(dudaConfigId, {
siteName: 'site_abc123',
accountName: 'acme-corp',
permissions: ['EDIT', 'PUBLISH'],
});
// Acme Corp can now edit and publish the site

Key Business Rules:

  • Multiple Permissions: Can grant multiple permissions at once
  • Overwrites Previous: New permission call overwrites previous permissions
  • Account Must Exist: Account must be created before granting access

getSSOLink(dudaHeader, account_name, params)โ€‹

Purpose: Generate a Single Sign-On (SSO) link for direct editor access without login

Source: services/accounts.service.js

External API Endpoint: GET https://api.duda.co/api/accounts/sso/:account_name/link

Parameters:

  • dudaHeader (ObjectId) - Config ID
  • account_name (String) - Duda account name
  • params (Object) - SSO parameters
    • target (String) - Destination: 'EDITOR', 'DASHBOARD', 'STATS'
    • site_name (String, optional) - Specific site to open
    • lang (String, optional) - Language code (e.g., 'en', 'es')

Returns: Promise<Object>

{
url: String; // Time-limited SSO link
}

Business Logic Flow:

  1. Fetch Credentials

    let query = await Config.findById(dudaHeader);
    if (!query) {
    throw notFound('Api token not found');
    }
  2. Generate SSO Link

    const access_token = utils.base64(query);

    let options = {
    method: 'GET',
    url: `https://api.duda.co/api/accounts/sso/${account_name}/link`,
    headers: {
    'Content-Type': 'application/json',
    authorization: `Basic ${access_token}`,
    },
    params, // target, site_name, lang
    };
    const response = await axios(options);

    return response.data;

API Request Example:

GET https://api.duda.co/api/accounts/sso/acme-corp/link?target=EDITOR&site_name=site_abc123
Authorization: Basic {base64_token}

API Response Example:

{
"url": "https://dashboard.duda.co/sso/abc123xyz?token=..."
}

SSO Targets:

  • EDITOR: Opens site editor for specific site (requires site_name)
  • DASHBOARD: Opens account dashboard (all sites)
  • STATS: Opens site analytics (requires site_name)

Example Usage:

// Editor access for specific site
const ssoData = await accountsService.getSSOLink(dudaConfigId, 'acme-corp', {
target: 'EDITOR',
site_name: 'site_abc123',
lang: 'en',
});

// Redirect user to SSO link
res.redirect(ssoData.url);

Key Business Rules:

  • Time-Limited: SSO links expire after a short period (check Duda docs)
  • Single Use: Links may be single-use only
  • Requires Site Access: Account must have permissions for the site
  • Target Required: Must specify target destination
  • Site Required for EDITOR/STATS: Must provide site_name for EDITOR and STATS targets

๐Ÿ”€ Integration Pointsโ€‹

Client Onboarding Workflowโ€‹

// Complete client onboarding
async function onboardClient(dudaConfigId, clientName, siteId) {
// 1. Create Duda account
await accountsService.createAccount(dudaConfigId, clientName);

// 2. Grant site access
await accountsService.grantSiteAccess(dudaConfigId, {
siteName: siteId,
accountName: clientName,
permissions: ['EDIT', 'PUBLISH'],
});

// 3. Generate SSO link for immediate access
const ssoData = await accountsService.getSSOLink(dudaConfigId, clientName, {
target: 'EDITOR',
site_name: siteId,
});

return ssoData.url;
}

Embeddable Editorโ€‹

// Embed Duda editor in iframe
const ssoData = await accountsService.getSSOLink(dudaConfigId, accountName, {
target: 'EDITOR',
site_name: siteId,
});

// Display in iframe
const iframeHtml = `<iframe src="${ssoData.url}" width="100%" height="800px"></iframe>`;

๐Ÿงช Edge Cases & Special Handlingโ€‹

Account Already Existsโ€‹

Issue: Account name already taken in Duda
Handling: Duda API returns error, propagated to caller

// Duda returns 409 Conflict
// Code doesn't handle duplicates explicitly

Missing Permissionsโ€‹

Issue: Permissions parameter empty or undefined
Handling: Throws 400 Bad Request

if (!permissions) {
throw badRequest('Invalid request body.');
}

Invalid Targetโ€‹

Issue: Invalid SSO target specified
Handling: Duda API returns error

Issue: User clicks expired SSO link
Handling: Duda shows error page, regenerate link

โš ๏ธ Important Notesโ€‹

  1. Account Type: Always creates CUSTOMER type accounts (not STAFF or AGENCY)
  2. Account Names: Must be unique across entire Duda platform
  3. Permission Overwrite: New permissions replace old ones, not additive
  4. SSO Expiration: Links expire quickly (minutes), generate on-demand
  5. Single Use Links: SSO links may be single-use, don't cache
  6. Site Access Required: Account needs permissions before SSO to EDITOR/STATS
  7. No Account Data: Create account doesn't return account details
  8. Language Support: SSO supports multiple languages via lang parameter
  9. Target Validation: EDITOR and STATS targets require site_name parameter
  10. Security: SSO links bypass authentication, protect them carefully
๐Ÿ’ฌ

Documentation Assistant

Ask me anything about the docs

Hi! I'm your documentation assistant. Ask me anything about the docs!

I can help you with:
- Code examples
- Configuration details
- Troubleshooting
- Best practices

Try asking: How do I configure the API?
09:30 AM