Skip to main content

Yext Entities Service

๐Ÿ“– Overviewโ€‹

The Yext Entities service manages business entities (locations) in the Yext platform. An entity represents a physical or virtual business location that can be distributed to 150+ publisher directories. This service handles complete CRUD operations, service activation, entity verification, listing management, analytics, and publisher logo integration.

Source File: external/Integrations/Yext/services/entities.service.js
External API: Yext Entities API & Listings API
Primary Use: Create and manage business locations for directory listing distribution

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

accountโ€‹

  • Operations: Read, Update
  • Model: external/models/account.js
  • Usage Context: Store Yext entity ID and verify account ownership
  • Key Fields:
    • yext.entity - Yext entity/location ID
    • yext_status - Service activation status
  • Operations: Read
  • Model: external/models/yext-publishers-logo.js
  • Usage Context: Join publisher logos to listing data
  • Key Fields:
    • publisherId - Unique publisher identifier
    • logo - Logo image URL

๐Ÿ”„ Data Flowโ€‹

sequenceDiagram
participant Client
participant EntitiesService
participant AccountDB
participant YextAPI
participant LogoDB

Client->>EntitiesService: Create entity
EntitiesService->>AccountDB: Check for existing entity
alt Entity exists
EntitiesService-->>Client: 400 Error (already assigned)
else No entity
EntitiesService->>YextAPI: POST /entities
YextAPI-->>EntitiesService: Entity created (meta.id)
EntitiesService->>AccountDB: Save entity ID
EntitiesService->>YextAPI: POST /existinglocationaddrequests
Note over EntitiesService,YextAPI: Activate SKU: LC-00000019
YextAPI-->>EntitiesService: Service activated
EntitiesService-->>Client: Entity created
end

Client->>EntitiesService: Get listings
EntitiesService->>YextAPI: GET /listings/listings
YextAPI-->>EntitiesService: Listings array
EntitiesService->>LogoDB: Get all logos
LogoDB-->>EntitiesService: Logo map
EntitiesService->>EntitiesService: Join logos by publisherId
EntitiesService-->>Client: Listings with logos

style EntitiesService fill:#e3f2fd
style YextAPI fill:#fff4e6

๐Ÿ”ง Business Logic & Functionsโ€‹

save(docToSave, accountID)โ€‹

Purpose: Save Yext entity ID to account document after entity creation

Source: services/entities.service.js

Parameters:

  • docToSave (Object) - Yext API response
    • response.meta.id (String) - Yext entity ID
  • accountID (ObjectId) - DashClicks account ID

Returns: Promise<Boolean> - Returns true on success

Business Logic Flow:

  1. Extract Entity ID

    let dataToSave = {
    entity: docToSave.response.meta.id,
    };
  2. Update Account Document

    await Account.findByIdAndUpdate(accountID, {
    $set: { yext: dataToSave },
    });

    return true;

Database Update:

// Account document structure
{
_id: ObjectId("507f1f77bcf86cd799439011"),
yext: {
entity: "12345678" // Yext entity ID
}
}

Key Business Rules:

  • Required Field: response.meta.id must exist in API response
  • Overwrites: Replaces existing yext object if present
  • No Validation: No validation before saving entity ID

activateService(id)โ€‹

Purpose: Activate Yext Listings service for a created entity with SKU LC-00000019

Source: services/entities.service.js

External API Endpoint: POST https://api.yext.com/v2/accounts/me/existinglocationaddrequests

Parameters:

  • id (String) - Yext entity/location ID

Returns: Promise<Object> - Yext API response

Business Logic Flow:

  1. Build Activation Request

    const url = 'https://api.yext.com/v2/accounts/me/existinglocationaddrequests';
    const sku = 'LC-00000019'; // Listings service SKU
    const v = process.env.YEXT_API_VPARAM || '20200525';

    const options = {
    method: 'POST',
    headers: { 'api-key': process.env.YEXT_API_KEYS },
    data: {
    existingLocationId: id,
    skus: [sku],
    },
    params: { v },
    url,
    };
  2. Activate Service

    const response = await axios(options);
    return response.data;

API Request Example:

POST https://api.yext.com/v2/accounts/me/existinglocationaddrequests?v=20200525
Headers:
api-key: {YEXT_API_KEY}
Content-Type: application/json
Body:
{
"existingLocationId": "12345678",
"skus": ["LC-00000019"]
}

API Response Example:

{
"meta": {
"uuid": "abc-def-123",
"errors": []
},
"response": {
"requestId": "req_xyz789"
}
}

Key Business Rules:

  • Fixed SKU: Always uses SKU LC-00000019 for listings service
  • Required After Creation: Must be called after entity creation
  • Enables Distribution: Activates listing distribution to publishers

verifyEntityId(entityId, accountID)โ€‹

Purpose: Verify that an entity ID belongs to a specific account (ownership check)

Source: services/entities.service.js

Parameters:

  • entityId (String) - Yext entity ID to verify
  • accountID (ObjectId) - DashClicks account ID

Returns: Promise<Boolean> - true if entity belongs to account, false otherwise

Business Logic Flow:

  1. Fetch Account

    const query = await Account.findById(accountID).lean().exec();
  2. Verify Ownership

    if (query) {
    if (query.yext && query.yext.entity == entityId) {
    return true;
    } else {
    return false;
    }
    } else {
    return false;
    }

Verification Logic:

// Returns true only if:
// 1. Account exists
// 2. Account has yext object
// 3. yext.entity matches provided entityId

// Returns false if:
// - Account doesn't exist
// - Account has no yext object
// - Entity ID mismatch

Key Business Rules:

  • Security Check: Prevents unauthorized access to entities
  • Used in All Operations: Called before update, delete, get operations
  • Loose Comparison: Uses == instead of === (allows string/number comparison)

addLogo(listings)โ€‹

Purpose: Join publisher logos to listing array for display purposes

Source: services/entities.service.js

Parameters:

  • listings (Array) - Array of listing objects from Yext API
    • Each listing has publisherId field

Returns: Promise<Array> - Listings array with logo URLs added

Business Logic Flow:

  1. Fetch All Logos

    let logos = await YextPublishersLogo.find({});
  2. Create Logo Map

    logos = logos.reduce((old, curr) => {
    old[curr.publisherId] = curr;
    return old;
    }, {});
    // Result: { "123": { publisherId: "123", logo: "url" }, ... }
  3. Join Logos to Listings

    listings = listings.map(listing => {
    listing.logo = logos[listing.publisherId]?.logo;
    return listing;
    });

    return listings;

Example Transformation:

// Input listings
[
{
publisherId: '123',
publisherName: 'Google',
status: 'LIVE',
},
{
publisherId: '456',
publisherName: 'Facebook',
status: 'LIVE',
},
];

// After addLogo()
[
{
publisherId: '123',
publisherName: 'Google',
status: 'LIVE',
logo: 'https://example.com/google-logo.png', // Added
},
{
publisherId: '456',
publisherName: 'Facebook',
status: 'LIVE',
logo: 'https://example.com/facebook-logo.png', // Added
},
];

Key Business Rules:

  • Non-Blocking: Missing logos result in undefined, doesn't fail
  • Optional Enhancement: Logos are UI enhancement, not required data
  • In-Memory Join: Uses reduce for efficient lookup

create({ query, body, accountId })โ€‹

Purpose: Create a new Yext entity/location and activate listings service

Source: services/entities.service.js

External API Endpoint: POST https://api.yext.com/v2/accounts/me/entities

Parameters:

  • query (Object) - Query parameters (v, etc.)
  • body (Object) - Entity data
    • name (String) - Business name
    • address (Object) - Business address
    • phone (String) - Business phone
    • Additional entity fields
  • accountId (ObjectId) - DashClicks account ID

Returns: Promise<Object> - Yext API response with created entity

Business Logic Flow:

  1. Check for Existing Entity

    const account = await accountService.find(accountId);

    if (!account?.error) {
    throw badRequest('An entity id is already assigned to this account.');
    }
  2. Create Entity

    const v = process.env.YEXT_API_VPARAM || '20200525';
    const url = 'https://api.yext.com/v2/accounts/me/entities?' + querystring.encode({ v: v });

    const options = {
    method: 'POST',
    headers: { 'api-key': process.env.YEXT_API_KEYS },
    data: body,
    params: query,
    url,
    };

    const dns = await axios(options);
    const data = dns.data;
  3. Save Entity ID to Account

    await exports.save(data, accountId);
  4. Activate Listings Service

    await exports.activateService(data.response.meta.id);
  5. Return Response

    return data;

API Request Example:

POST https://api.yext.com/v2/accounts/me/entities?v=20200525
Headers:
api-key: {YEXT_API_KEY}
Content-Type: application/json
Body:
{
"name": "My Business",
"address": {
"line1": "123 Main St",
"city": "New York",
"region": "NY",
"postalCode": "10001",
"countryCode": "US"
},
"phone": "+1-555-0100",
"categoryIds": ["12345"],
"websiteUrl": "https://example.com"
}

API Response Example:

{
"meta": {
"uuid": "abc-def-123",
"errors": [],
"id": "12345678" // Entity ID
},
"response": {
"name": "My Business",
"address": {...},
"id": "12345678"
}
}

Error Handling:

  • Entity Already Exists: Throws 400 Bad Request if account already has entity
  • API Errors: Yext API errors propagate up

Example Usage:

const entity = await entitiesService.create({
accountId: '507f1f77bcf86cd799439011',
body: {
name: 'Coffee Shop NYC',
address: {
line1: '123 Broadway',
city: 'New York',
region: 'NY',
postalCode: '10001',
countryCode: 'US',
},
phone: '+1-212-555-0100',
categoryIds: ['8040'], // Restaurant category
websiteUrl: 'https://coffeeshopnyc.com',
},
query: { v: '20200525' },
});

// Result:
// 1. Entity created in Yext
// 2. Entity ID saved to account.yext.entity
// 3. Listings service activated with SKU LC-00000019
// 4. Entity can now be distributed to publishers

Key Business Rules:

  • One Entity Per Account: Prevents creating multiple entities for same account
  • Automatic Activation: Immediately activates listings service after creation
  • Complete Flow: Handles creation, saving, and activation in single operation

retry({ accountId })โ€‹

Purpose: Retry listing activation for an account (utility function)

Source: services/entities.service.js

Parameters:

  • accountId (ObjectId) - DashClicks account ID

Returns: Promise<Object> - Result from activateListing utility

Business Logic Flow:

const entity = await activateListing(accountId, null);
return entity;

Key Business Rules:

  • Utility Wrapper: Wraps activateListing utility function
  • No Entity ID: Passes null as second parameter
  • Error Recovery: Used to retry failed activations

get({ entityId, query, accountId })โ€‹

Purpose: Retrieve entity details from Yext

Source: services/entities.service.js

External API Endpoint: GET https://api.yext.com/v2/accounts/me/entities/:entityId

Parameters:

  • entityId (String) - Yext entity ID
  • query (Object) - Query parameters
  • accountId (ObjectId) - DashClicks account ID

Returns: Promise<Object> - Entity details from Yext

Business Logic Flow:

  1. Validate Entity ID

    if (!entityId) {
    throw badRequest('Entity id is required.');
    }
  2. Verify Account Has Entity

    const account = await accountService.find(accountId);
    if (account?.error) {
    throw notFound(account?.message);
    }
  3. Verify Entity Ownership

    const verifyEntityId = await exports.verifyEntityId(entityId, accountId);
    if (!verifyEntityId) {
    throw badRequest('Entity Id mismatch!');
    }
  4. Fetch Entity from Yext

    const v = process.env.YEXT_API_VPARAM || '20200525';
    const url =
    'https://api.yext.com/v2/accounts/me/entities/' +
    entityId +
    '?' +
    querystring.encode({ v: v });

    const options = {
    method: 'GET',
    headers: { 'api-key': process.env.YEXT_API_KEYS },
    params: query,
    url,
    };

    const dns = await axios(options);
    return dns.data;

API Request Example:

GET https://api.yext.com/v2/accounts/me/entities/12345678?v=20200525
Headers:
api-key: {YEXT_API_KEY}

API Response Example:

{
"meta": {
"uuid": "abc-def-123",
"errors": []
},
"response": {
"id": "12345678",
"name": "My Business",
"address": {
"line1": "123 Main St",
"city": "New York",
"region": "NY",
"postalCode": "10001"
},
"phone": "+1-555-0100",
"categoryIds": ["8040"],
"websiteUrl": "https://example.com"
}
}

Error Handling:

  • Missing Entity ID: Throws 400 Bad Request
  • Account Not Found: Throws 404 Not Found
  • Entity Mismatch: Throws 400 Bad Request if entity doesn't belong to account

Key Business Rules:

  • Three-Step Validation: Required field, account check, ownership verification
  • Security: Prevents accessing other accounts' entities

update({ entityId, query, body, accountId })โ€‹

Purpose: Update entity information in Yext

Source: services/entities.service.js

External API Endpoint: PUT https://api.yext.com/v2/accounts/me/entities/:entityId

Parameters:

  • entityId (String) - Yext entity ID
  • query (Object) - Query parameters
  • body (Object) - Update data (partial entity fields)
  • accountId (ObjectId) - DashClicks account ID

Returns: Promise<Object> - Updated entity data from Yext

Business Logic Flow:

  1. Validate and Verify (same as get())

    • Check entity ID exists
    • Verify account has entity
    • Verify entity ownership
  2. Update Entity

    const url =
    'https://api.yext.com/v2/accounts/me/entities/' +
    entityId +
    '?' +
    querystring.encode({ v: v });

    const options = {
    method: 'PUT',
    headers: { 'api-key': process.env.YEXT_API_KEYS },
    data: body,
    params: query,
    url,
    };

    const dns = await axios(options);
    const data = dns.data;
  3. Re-save Entity ID

    await exports.save(data, accountId);
    return data;

API Request Example:

PUT https://api.yext.com/v2/accounts/me/entities/12345678?v=20200525
Headers:
api-key: {YEXT_API_KEY}
Content-Type: application/json
Body:
{
"phone": "+1-555-0200",
"websiteUrl": "https://newdomain.com"
}

Example Usage:

// Update phone and website
await entitiesService.update({
entityId: '12345678',
accountId: '507f1f77bcf86cd799439011',
body: {
phone: '+1-212-555-0200',
websiteUrl: 'https://newdomain.com',
},
query: { v: '20200525' },
});

Key Business Rules:

  • Partial Updates: Only provided fields are updated
  • Re-saves Entity ID: Updates account document with response (in case entity ID changes)
  • Same Validation: Uses identical validation as get()

delete({ entityId, query, body, accountId })โ€‹

Purpose: Delete entity from Yext and remove from account

Source: services/entities.service.js

External API Endpoint: DELETE https://api.yext.com/v2/accounts/me/entities/:entityId

Parameters:

  • entityId (String) - Yext entity ID
  • query (Object) - Query parameters
  • body (Object) - Delete options
  • accountId (ObjectId) - DashClicks account ID

Returns: Promise<Boolean> - Returns true on success

Business Logic Flow:

  1. Validate and Verify (same as get/update)

  2. Delete from Yext

    const url =
    'https://api.yext.com/v2/accounts/me/entities/' +
    entityId +
    '?' +
    querystring.encode({ v: v });

    const options = {
    method: 'DELETE',
    headers: { 'api-key': process.env.YEXT_API_KEYS },
    data: body,
    params: query,
    url,
    };

    await axios(options);
  3. Remove from Account

    await Account.findByIdAndUpdate(accountId, { $unset: { yext: true } });

    return true;

API Request Example:

DELETE https://api.yext.com/v2/accounts/me/entities/12345678?v=20200525
Headers:
api-key: {YEXT_API_KEY}

Database Update:

// Before delete
{
_id: ObjectId("507f1f77bcf86cd799439011"),
yext: {
entity: "12345678"
}
}

// After delete
{
_id: ObjectId("507f1f77bcf86cd799439011")
// yext field completely removed
}

Key Business Rules:

  • Unsets Field: Uses $unset to completely remove yext field from account
  • Cascading Delete: Deletes from Yext first, then removes from account
  • No Rollback: No transaction - if account update fails, entity already deleted from Yext

listListingsByLocation({ params, accountId })โ€‹

Purpose: List all publisher listings for an entity/location

Source: services/entities.service.js

External API Endpoint: GET https://api.yext.com/v2/accounts/me/listings/listings

Parameters:

  • params (Object) - Query parameters
    • locationId (String) - Entity ID to get listings for
  • accountId (ObjectId) - DashClicks account ID

Returns: Promise<Object> - Listings data from Yext

Business Logic Flow:

  1. Verify Account

    const account = await accountService.find(accountId);
    if (account?.error) {
    throw notFound(account?.message);
    }
  2. Fetch Listings

    const url =
    'https://api.yext.com/v2/accounts/me/listings/listings?' + querystring.encode({ v: v });

    const options = {
    method: 'GET',
    headers: { 'api-key': process.env.YEXT_API_KEYS },
    params: params,
    url,
    };

    const dns = await axios(options);
    return dns.data;

API Request Example:

GET https://api.yext.com/v2/accounts/me/listings/listings?v=20200525&locationId=12345678
Headers:
api-key: {YEXT_API_KEY}

API Response Example:

{
"meta": {
"uuid": "abc-def-123",
"errors": []
},
"response": {
"count": 150,
"listings": [
{
"publisherId": "123",
"publisherName": "Google",
"locationId": "12345678",
"status": "LIVE",
"listingUrl": "https://google.com/business/...",
"syncStatus": "SYNCED"
},
{
"publisherId": "456",
"publisherName": "Facebook",
"locationId": "12345678",
"status": "WAITING_ON_PUBLISHER",
"syncStatus": "PENDING"
}
// ... 148 more publishers
]
}
}

Listing Statuses:

  • LIVE - Active on publisher
  • WAITING_ON_PUBLISHER - Pending publisher approval
  • OPTED_OUT - Opted out from this publisher
  • SUPPRESSED - Suppressed by Yext/publisher

Key Business Rules:

  • No Entity ID Required in Function: locationId passed in params
  • Returns All Publishers: Returns status for 150+ publishers
  • Account Verification Only: Doesn't verify entity ownership (uses params.locationId)

entityOpt({ entityId, query, accountId })โ€‹

Purpose: Opt-in or opt-out entity from publisher listings

Source: services/entities.service.js

External API Endpoint: PUT https://api.yext.com/v2/accounts/me/listings/listings/{action}

Parameters:

  • entityId (String) - Yext entity ID
  • query (Object) - Query parameters
    • action (String) - 'optin' or 'optout'
    • publisherIds (String) - Comma-separated publisher IDs
  • accountId (ObjectId) - DashClicks account ID

Returns: Promise<Object> - API response or empty object on error

Business Logic Flow:

  1. Validate and Verify (same as get/update)

  2. Determine Action

    let action = 'optin'; // default

    if (typeof query.action !== 'undefined' && query.action != '') {
    if (query.action == 'optin') {
    action = 'optin';
    }
    if (query.action == 'optout') {
    action = 'optout';
    }
    }
  3. Execute Opt Action

    let dns = {};
    try {
    const url =
    'https://api.yext.com/v2/accounts/me/listings/listings/' +
    action +
    '?' +
    querystring.encode({ v: v });

    const options = {
    method: 'PUT',
    headers: { 'api-key': process.env.YEXT_API_KEYS },
    params: query,
    url,
    };

    dns = await axios(options);
    } catch (error) {
    dns.data = {};
    }

    return dns.data;

API Request Example:

PUT https://api.yext.com/v2/accounts/me/listings/listings/optin?v=20200525&locationId=12345678&publisherIds=123,456
Headers:
api-key: {YEXT_API_KEY}

Error Handling:

  • Graceful Failure: Returns empty object {} on error instead of throwing
  • No Error Propagation: Errors swallowed silently

Example Usage:

// Opt-in to Google and Facebook
await entitiesService.entityOpt({
entityId: '12345678',
accountId: '507f1f77bcf86cd799439011',
query: {
action: 'optin',
publisherIds: '123,456',
locationId: '12345678',
v: '20200525',
},
});

// Opt-out of Yelp
await entitiesService.entityOpt({
entityId: '12345678',
accountId: '507f1f77bcf86cd799439011',
query: {
action: 'optout',
publisherIds: '789',
locationId: '12345678',
v: '20200525',
},
});

Key Business Rules:

  • Defaults to Optin: If action not specified or invalid, defaults to 'optin'
  • Multiple Publishers: Can opt-in/out multiple publishers at once
  • Silent Errors: Errors don't throw, returns empty object

getReport({ body, entityId, accountId })โ€‹

Purpose: Generate analytics report for entity listings

Source: services/entities.service.js

External API Endpoint: POST https://api.yext.com/v2/accounts/me/analytics/reports

Parameters:

  • body (Object) - Report configuration
    • metrics (Array) - Metrics to include
    • dimensions (Array) - Dimensions to group by
    • filters (Object) - Filters to apply
  • entityId (String) - Yext entity ID
  • accountId (ObjectId) - DashClicks account ID

Returns: Promise<Object> - Analytics report data

Business Logic Flow:

  1. Validate and Verify (same as get/update)

  2. Generate Report

    const url = 'https://api.yext.com/v2/accounts/me/analytics/reports';

    const options = {
    method: 'POST',
    headers: { 'api-key': process.env.YEXT_API_KEYS },
    params: { v: v },
    data: body,
    url,
    };

    const dns = await axios(options);
    return dns.data;

API Request Example:

POST https://api.yext.com/v2/accounts/me/analytics/reports?v=20200525
Headers:
api-key: {YEXT_API_KEY}
Content-Type: application/json
Body:
{
"metrics": ["LISTINGS_LIVE", "STOREPAGES_PAGEVIEWS"],
"dimensions": ["PUBLISHER_ID"],
"filters": {
"locationIds": ["12345678"],
"startDate": "2024-01-01",
"endDate": "2024-01-31"
}
}

Available Metrics (examples):

  • LISTINGS_LIVE - Number of live listings
  • STOREPAGES_PAGEVIEWS - Page views on store pages
  • SEARCH_QUERIES - Number of search queries
  • PROFILE_VIEWS - Profile views
  • DIRECTION_REQUESTS - Direction requests

Key Business Rules:

  • Flexible Reporting: Supports various metrics and dimensions
  • Entity Verification: Verifies entity ownership before generating report

deactivateEntity({ entityId, accountId })โ€‹

Purpose: Deactivate/cancel services for an entity

Source: services/entities.service.js

External API Endpoint: POST https://api.yext.com/v2/accounts/me/cancelservices

Parameters:

  • entityId (String) - Yext entity ID
  • accountId (ObjectId) - DashClicks account ID

Returns: Promise<Object> - Yext API response

Business Logic Flow:

  1. Validate and Verify (same as get/update)

  2. Cancel Services

    const url = 'https://api.yext.com/v2/accounts/me/cancelservices';

    const options = {
    method: 'POST',
    headers: { 'api-key': process.env.YEXT_API_KEYS },
    data: {
    locationId: entityId,
    },
    params: { v },
    url,
    };

    const response = await axios(options);
    return response.data;

API Request Example:

POST https://api.yext.com/v2/accounts/me/cancelservices?v=20200525
Headers:
api-key: {YEXT_API_KEY}
Content-Type: application/json
Body:
{
"locationId": "12345678"
}

Key Business Rules:

  • Service Cancellation: Cancels active services for entity
  • Different from Delete: Deactivates services but doesn't delete entity
  • Verification Required: Verifies entity ownership before deactivation

๐Ÿ”€ Integration Pointsโ€‹

Internal Servicesโ€‹

  • Account Service: Verifies account entity initialization
  • Listings Utility: utilities/listings.js - Retry activation logic

Complete Entity Lifecycleโ€‹

// 1. Create entity
const entity = await entitiesService.create({
accountId: accountId,
body: businessData,
query: { v: '20200525' },
});
// Auto-saves entity ID and activates service

// 2. Get entity details
const details = await entitiesService.get({
entityId: entity.response.meta.id,
accountId: accountId,
query: { v: '20200525' },
});

// 3. Update entity
await entitiesService.update({
entityId: entityId,
accountId: accountId,
body: { phone: '+1-555-0200' },
query: { v: '20200525' },
});

// 4. Get listings status
const listings = await entitiesService.listListingsByLocation({
accountId: accountId,
params: { locationId: entityId, v: '20200525' },
});

// Add logos for display
const listingsWithLogos = await entitiesService.addLogo(listings.response.listings);

// 5. Opt-out of specific publishers
await entitiesService.entityOpt({
entityId: entityId,
accountId: accountId,
query: {
action: 'optout',
publisherIds: '789',
locationId: entityId,
v: '20200525',
},
});

// 6. Generate analytics report
const report = await entitiesService.getReport({
entityId: entityId,
accountId: accountId,
body: {
metrics: ['LISTINGS_LIVE'],
dimensions: ['PUBLISHER_ID'],
filters: { locationIds: [entityId] },
},
});

// 7. Deactivate services
await entitiesService.deactivateEntity({
entityId: entityId,
accountId: accountId,
});

// 8. Delete entity
await entitiesService.delete({
entityId: entityId,
accountId: accountId,
query: { v: '20200525' },
body: {},
});

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

Entity Already Existsโ€‹

Issue: Attempt to create entity when account already has one
Handling: Throws 400 Bad Request

if (!account?.error) {
throw badRequest('An entity id is already assigned to this account.');
}

Entity ID Mismatchโ€‹

Issue: Entity ID doesn't belong to account
Handling: Throws 400 Bad Request on all operations

Prevention: Always verify entity ownership before operations

Missing Entity Initializationโ€‹

Issue: Account doesn't have yext.entity field
Handling: accountService.find() returns error object

Solution: Create entity first using create()

Graceful Opt-In/Out Failuresโ€‹

Issue: Opt-in/out API call fails
Handling: Returns empty object instead of throwing

Why: Prevents breaking UI if opt operation fails

โš ๏ธ Important Notesโ€‹

  1. Fixed SKU: Always uses LC-00000019 for listings service activation
  2. One Entity Per Account: Enforced at application level
  3. Automatic Activation: create() automatically activates service
  4. Three-Step Validation: All operations validate: required fields โ†’ account โ†’ ownership
  5. Logo Enhancement: addLogo() is optional UI enhancement
  6. Graceful Opt Errors: entityOpt() doesn't throw errors
  7. No Rollback: Delete removes from Yext first, then account
  8. Entity Verification: Uses loose comparison (==) for entity ID matching
  9. API Versioning: All requests include v parameter
  10. Publisher IDs: Comma-separated string for opt-in/out operations
๐Ÿ’ฌ

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:31 AM