Cortex Skills
  • Skills Directory
  • Categories
  • Docs
Add SkillBrowse Skills
  1. Home
  2. Skills Directory
  3. Cortex Analyst
  4. internal-marketplace-org-listing
IntermediateCortex Analystfeatured

internal-marketplace-org-listing

Jump to Installation

Compatible with

Cortex

About this skill

Organizational Listing Provider Skill

Create and publish organizational listings to share data products across accounts within your Snowflake organization via Internal Marketplace.

When to Use

USE THIS SKILL when:

  • Sharing objects with OTHER ACCOUNTS in the organization
  • User mentions "internal marketplace" or "data product" (even for same account)
  • Creating internal marketplace listings as a data provider
  • Publishing data products to internal consumers
  • Cross-region auto-fulfillment setup

USE RBAC (not this skill) when:

  • User wants to share with roles in the SAME account only
  • User does NOT mention "internal marketplace" or "data product"
  • Example: "grant access to ANALYST role" → Use GRANT command, not this skill

Common triggers: "share to internal marketplace", "create a data product", "share with other accounts", "publish to internal marketplace"

Documentation: Organization Listing Docs

Quick Flow (Minimal Input)

When user says something like "share my agent to internal marketplace" or "share this object internally":

  1. Identify the object(s) the user wants to share
  2. Ask for required info:
    To create the listing, I need:
    1. Who should have access? (all accounts / specific accounts)
    2. What email should I use for support and approver contacts?
    
  3. Auto-generate listing with minimal fields:
    • Title: create a meaningful title that describes the data product and all objects included
    • Description: auto-generated a helpful description on what this data product can do, what objects it includes, and what use cases it can help address
    • Discovery/Access: based on user's input
    • Contacts: use the email provided by the user for both support and approver if only one email is provided, otherwise use the email to the specific contact field specified by the user.
    • Request approval flow: Include request_approval_type if user explicitly specifies how approvals should be handled
    • Data Dictionary: Add data dictionary for all tables and views added in the data product
  4. Skip data dictionary for non-table objects (agents, semantic views, functions)
  5. Create and publish immediately after confirmation

⚠️ Note: Data dictionary and usage examples are only applicable for tables/views. Skip these for agents, semantic views, functions, and other non-queryable objects.

⚠️ Cortex Agent Sharing Limitations: Cortex Agents CANNOT be shared if they:

  • Use a custom warehouse in agent spec or tools
  • Have tools in different databases
  • Have custom query_timeout settings
  • Have an invalid agent spec

If agent sharing fails, suggest sharing the underlying tables instead.

Prerequisites

  1. Organization Setup:

    • Account must be part of a Snowflake organization with ORGADMIN role
    • Know your organization's account names (SHOW ORGANIZATION ACCOUNTS)
  2. Required Privileges:

    • CREATE SHARE on ACCOUNT
    • CREATE ORGANIZATION LISTING on ACCOUNT
    • USAGE WITH GRANT OPTION on database/schema to share
    • MANAGE LISTING AUTO FULFILLMENT on ACCOUNT (if cross-region)

Verify with:

SELECT CURRENT_ROLE();
SHOW GRANTS TO ROLE <your_role>;

Workflow

Start → Step 1: Gather → Step 2: Create Share → Step 3: Create Listing → Step 4: Verify → Done
            ↑                                         ↑
      ⚠️ STOP                                   ⚠️ STOP

Step 1: Gather Requirements

Goal: Collect all information needed to create the share and listing.

Actions:

  1. Ask the user:

    To create your organizational listing, please provide:
    
    1. **Objects to share**: Which database/schema/tables/views/semantic views?
       (Please list the EXACT objects - only these will be added to the share)
    2. **Access**: Who should have access?
       - All internal accounts (or user already said "share with all accounts")
       - Specific accounts only (please list them)
    3. **Contact email**: What email should be used for support and approver contacts?
    4. **Organization profile** Which organization profile should be used for this listing? 
       - The system-generated default INTERNAL profile
       - An available custom profile in your organization (please specify the name)
    
  2. Auto-generate (do not ask user for these):

    • Title: Create a meaningful title that describes the data product and all objects included
    • Description: Generate a helpful description explaining what this data product offers, what objects it includes, and what use cases it can address
    • Data Dictionary: Add data dictionary for all tables and views in the data product
    • Support & Approver contacts:
      • If the user provides TWO distinct emails with labels (e.g., "Support Contact: email1" and "Approver Contact: email2"), map each email to its corresponding field (support_contact = email1, approver_contact = email2)
      • If the user provides only ONE email, use it for BOTH support_contact and approver_contact fields

    ⚠️ CRITICAL: Only share objects the user explicitly lists. Never add:

    • INFORMATION_SCHEMA
    • System schemas or tables
    • Objects not explicitly requested by the user
  3. If user asks to share "all objects in a schema", discover them:

    -- Get all tables
    SHOW TABLES IN SCHEMA <database>.<schema>;
    
    -- Get all views
    SHOW VIEWS IN SCHEMA <database>.<schema>;
    
    -- Get all semantic views (NOT included in SHOW VIEWS)
    SHOW SEMANTIC VIEWS IN SCHEMA <database>.<schema>;
    

    Compile the list from all three commands, then confirm with user before proceeding.

  4. If user mentions sharing to accounts or targeting accounts, or if user mentions targeting/sharing to regions

    Step A: Verify whether a specified account is a valid account in the organization: Fetch all the accounts in the organization by running:

    SHOW ACCOUNTS;
    -- record query id
    

    ⚠️ CRITICAL: DO NOT run SHOW ORGANIZATION ACCOUNTS to fetch the accounts. ⚠️ CRITICAL: SHOW ACCOUNTS output may be truncated in large organizations. If the result indicates truncation (e.g., "N row(s) shown but more may have been returned"), always use RESULT_SCAN with a WHERE filter to search for specific accounts in the response of SHOW ACCOUNTS rather than scanning the raw output visually:

    -- use the returned `account_name` and `snowflake_region` for the following steps.
    SELECT "account_name", "account_locator", "snowflake_region"
    FROM TABLE(RESULT_SCAN(<query id of show accounts>))
    WHERE UPPER("account_name") = '<ACCOUNT>' OR UPPER("account_locator") = '<ACCOUNT>';
    

    If the user specified the account name in the format of "<organization name>.<account alias>", use the account alias as the account name for account name verification and following steps if the organization name is the same as the current account. ALWAYS use account_name instead of account_locator in the following steps even when the user specified the account locator.

    Step B: Use the exact account name in organization_targets:

    ⚠️ CRITICAL - Account Name Format:

    • Use ONLY the account_name from SHOW ACCOUNTS
    • NEVER append region names
    • NEVER use account locators
    organization_targets:
      discovery:
        - account: "HR"  # Use exact account_name from SHOW ACCOUNTS
      access:
        - account: "HR"  
    

    ⚠️ OPTIMIZATION - For "current account" or "same account":

    When instruction mentions "current account", "same account", or "roles in this account", you can use CURRENT_ACCOUNT_NAME() directly without needing to query SHOW ACCOUNTS:

    SELECT CURRENT_ACCOUNT_NAME() as account_name;
    

    Use the returned value directly in organization_targets:

    organization_targets:
      access:
        - account: "PM_AWS_US_WEST_2"  # From CURRENT_ACCOUNT_NAME()
          roles: ['ACCOUNTADMIN', 'SYSADMIN']
    
  5. Verify the specified organization profile:

    Find all the organization profiles available in this organization:

    SHOW AVAILABLE ORGANIZATION PROFILES;
    -- Convert the user-specified organization profile name to all uppercase if needed and look for the exact organization profile name from the 'name' column.
    

    ⚠️ CRITICAL: An organization profile is only available for publishing listings when the exact name matches with the uppercase format of the user-specified name, and the 'can_publish_listings_with_profile' column for this organization profile is true.

    If the specified organization profile is not found, list the names of the available organization profiles with the 'can_publish_listings_with_profile' column as true and ask the user to choose from one of these options.

  6. Get current region (needed for locations):

    SELECT CURRENT_REGION();
    

⚠️ MANDATORY STOPPING POINT: Do NOT proceed until user provides all required information.


Step 2: Create the Share

Goal: Create the underlying share with correct privilege grants.

╔══════════════════════════════════════════════════════════════════════════════╗
║  ⚠️ CRITICAL: GRANT ORDER MATTERS - FOLLOW EXACTLY OR SHARE WILL FAIL       ║
║                                                                              ║
║  1. FIRST:  GRANT USAGE ON DATABASE  ← Must be first!                        ║
║  2. SECOND: GRANT USAGE ON SCHEMA                                            ║
║  3. LAST:   GRANT SELECT ON TABLE/VIEW/SEMANTIC VIEW                         ║
║                                                                              ║
║  Error "Share does not currently have a database" = Wrong order!             ║
╚══════════════════════════════════════════════════════════════════════════════╝

⚠️ CRITICAL: Only add objects the user explicitly specifies to the share.

  • Do NOT add INFORMATION_SCHEMA
  • Do NOT add system schemas
  • Do NOT add objects the user didn't request
  • Ask user to confirm the exact list of objects before creating the share

Actions:

  1. Create share:

    CREATE SHARE IF NOT EXISTS <share_name>
      COMMENT = '<description of what is being shared>';
    
  2. Grant privileges (in order!):

    -- FIRST: Database
    GRANT USAGE ON DATABASE <database_name> TO SHARE <share_name>;
    
    -- SECOND: Schema
    GRANT USAGE ON SCHEMA <database_name>.<schema_name> TO SHARE <share_name>;
    
    -- LAST: Tables/Views/Semantic Views
    -- For tables:
    GRANT SELECT ON TABLE <database_name>.<schema_name>.<table> TO SHARE <share_name>;
    -- Or for all tables:
    GRANT SELECT ON ALL TABLES IN SCHEMA <database_name>.<schema_name> TO SHARE <share_name>;
    
    -- ⚠️ VIEWS: Must grant individually (bulk grant on views is restricted)
    GRANT SELECT ON VIEW <database_name>.<schema_name>.<view> TO SHARE <share_name>;
    -- NOTE: "GRANT SELECT ON ALL VIEWS" is NOT supported for shares
    
    -- ⚠️ SEMANTIC VIEWS: Use SELECT (not USAGE)
    GRANT SELECT ON SEMANTIC VIEW <database_name>.<schema_name>.<semantic_view> TO SHARE <share_name>;
    

    ⚠️ Finding Semantic Views: Use SHOW SEMANTIC VIEWS (not SHOW VIEWS):

    SHOW SEMANTIC VIEWS IN SCHEMA <database_name>.<schema_name>;
    

    If error "Non-secure object can only be granted to shares with "secure_objects_only" property set to false." happens when granting any of the tables, views, or functions to the share → List all the options and ask the user to confirm how they want to proceed with the share creation:

    • Option 1: Alter the share to allow sharing non-secure objects. Show a bold warning with this option that a share cannot set secure_objects_only to true once it's set to false, execute
    ALTER SHARE <share_name> SET SECURE_OBJECTS_ONLY = FALSE;
    
    • Option 2: Convert this object to a secure object. Show a bold warning with this option that users should weigh the trade-off between data privacy/security and query performance before proceeding. If the user chooses option 2, execute:
    ALTER VIEW <database_name>.<schema_name>.<view> SET SECURE;
    
    • Option 3: Skip granting this non-secure object to the share.
  3. Verify share contents:

    DESCRIBE SHARE <share_name>;
    

Output: Share created with all requested objects granted.

If error "Share does not currently have a database" → Check grant order (database must be first).

⚠️ Metadata Visibility Note: Granting USAGE ON DATABASE makes all schema names visible to consumers in metadata, even if they can't query objects in those schemas.


Step 3: Create the Listing

Goal: Create organizational listing with YAML manifest including data dictionary.

Actions:

  1. Organization Targets - Discovery & Access:

    ⚠️ CRITICAL - How to handle discovery targets:

    • If instruction says "Do not allow anyone to discover" or "No discovery" → OMIT the discovery field entirely from organization_targets:

      organization_targets:
        access:
          - account: "ACCOUNT_NAME"
        # NO discovery field when discovery is disabled
      
    • If instruction says "all accounts in the organization" for discovery or access → use all_internal_accounts: true:

      organization_targets:
        discovery:
          - all_internal_accounts: true
        access:
          - all_internal_accounts: true
      
    • If instruction specifies specific accounts for discovery or access → list them:

      organization_targets:
        discovery:
          - account: "ACCOUNT_1"
        access:
          - account: "ACCOUNT_1"
      

    ⚠️ CRITICAL - Role-based access:

    • If instruction mentions roles (e.g., "ACCOUNTADMIN role in PM_SHARING" or "ACCOUNTADMIN and SYSADMIN roles") → include roles field in the following format:
      organization_targets:
        access:
          - account: "PM_SHARING"
            roles: ['ACCOUNTADMIN', 'SYSADMIN']
      
  2. Access Regions:

    ⚠️ The listing owner can specify any access regions they want. This is independent of auto-fulfillment.

    How to choose access regions:

    • If instruction explicitly says "all regions" or "target all regions":

      • Use the literal value ALL:
    • If instruction specifies specific regions:

      • Use those specific regions (e.g., PUBLIC.AWS_US_WEST_2, PUBLIC.AWS_US_EAST_1)
    • If instruction doesn't specify regions:

      • Default to the current account's region (e.g., PUBLIC.AWS_US_WEST_2)

    Note: Access region choice does NOT determine auto-fulfillment. See section 5 below for auto-fulfillment logic.

    When using specific access regions:

    • If user requests targeting specific regions, or mentions targeting only the target accounts' regions or locations, or mentions targeting the minimal set of regions possible, add the access region names to the manifest.

      Each access region name should be in the format of "<region_group>.<snowflake_region>", e.g., "PUBLIC.AWS_US_WEST_2". If the user specified a set of regions, use the specified region list; otherwise add the regions of all targeted accounts without duplication. If the region group is not specified for any snowflake regions, run:

      SHOW REGIONS IN DATA EXCHANGE SNOWFLAKE_DATA_MARKETPLACE;
      

      and use the values in the region_group field in the response for the corresponding snowflake_regions of the accounts.

      If any of the target accounts are outside the access regions, list the following options and ask user for supplemental information: - Skip this target account that is not in any of the specified access regions. - Add the access region "<region_group>.<snowflake_region>" to the access regions

    Add the access regions to the manifest in the following format:

       locations:
       access_regions:
          - name: "<REGION_NAME>"
          - name: "<REGION_NAME_2>"
    
  3. Auto-select tables for data dictionary (up to 5):

    ⚠️ SKIP this step if sharing non-table objects (agents, semantic views, functions). Data dictionary is only supported for tables and views.

    • Query the share to identify objects:

      DESCRIBE SHARE <share_name>;
      
    • Prioritize (select up to 5 most relevant):

      • Main fact tables (transactions, events, orders)
      • Key dimension tables (customers, products)
      • Commonly queried views
      • Aggregated/summary tables
    • Exclude: staging tables, internal/system tables, rarely used lookups

    • Auto-detect PII fields in selected objects (tables only):

      -- Check column names for PII patterns
      DESCRIBE TABLE <database>.<schema>.<table>;
      
      -- If available, check Snowflake classification tags
      SELECT * FROM TABLE(
        INFORMATION_SCHEMA.TAG_REFERENCES('<database>.<schema>.<table>', 'TABLE')
      );
      
    • Common PII patterns to detect:

      • Names: first_name, last_name, full_name, customer_name
      • Contact: email, phone, mobile, address, zip, postal
      • IDs: ssn, social_security, tax_id, passport, driver_license
      • Financial: credit_card, account_number, bank_account
      • Health: dob, date_of_birth, medical_id, patient_id
    • Note PII fields in description for consumer awareness

  4. Auto-generate SQL usage examples (tables/views only):

    ⚠️ SKIP if no tables/views in the data product (e.g., only agents or functions).

    ╔══════════════════════════════════════════════════════════════════════════╗
    ║  ⚠️ MANDATORY: Run DESCRIBE TABLE for EACH table BEFORE writing queries ║
    ║                                                                          ║
    ║  NEVER assume column names! Get the ACTUAL column names first.           ║
    ╚══════════════════════════════════════════════════════════════════════════╝
    

    Step 0: Get ACTUAL column names (MANDATORY)

    -- Run this for EACH table before writing any usage examples
    DESCRIBE TABLE <database>.<schema>.<table>;
    

    Use ONLY the column names returned by DESCRIBE. Never guess or assume.

    Step 2: Think about what questions users would ask this data

    Based on the table/column names, deduce what the data represents and what insights users would want:

    Data TypeExample TablesQuestions Users Would AskQuery Pattern
    Sales/Ordersorders, transactions, sales"What's the revenue by region?" "Top customers?"GROUP BY with SUM, ranking
    Customer/Usercustomers, users, accounts"How many active users?" "Customer segments?"COUNT, segmentation, cohorts
    Events/Logsevents, logs, activity"What happened last 7 days?" "Error rate?"Time filters, COUNT by type
    Product/Inventoryproducts, inventory, catalog"What's in stock?" "Top products?"JOINs, availability checks
    Financialinvoices, payments, budgets"Monthly spend?" "Outstanding balance?"SUM, date aggregations

    Step 3: Generate 2-3 meaningful queries

    Rules:

    • ALWAYS use fully qualified table names: DATABASE.SCHEMA.TABLE
    • NEVER use SELECT * - select specific, useful columns
    • Include aggregations (SUM, COUNT, AVG) with GROUP BY
    • Include JOINs if multiple related tables exist
    • Include date filters for time-series data
    • Validate SQL compiles before adding to manifest

    Example - For a CALLS table with columns (call_id, agent_id, duration_seconds, created_at, status):

    -- Example 1: Call volume and average duration by day
    SELECT 
      DATE_TRUNC('day', created_at) as call_date,
      COUNT(*) as total_calls,
      AVG(duration_seconds) as avg_duration_sec,
      SUM(CASE WHEN status = 'completed' THEN 1 ELSE 0 END) as completed_calls
    FROM MYDB.MYSCHEMA.CALLS
    WHERE created_at >= DATEADD('day', -30, CURRENT_DATE)
    GROUP BY 1
    ORDER BY 1 DESC;
    
    -- Example 2: Top agents by call volume
    SELECT 
      agent_id,
      COUNT(*) as total_calls,
      SUM(duration_seconds) as total_duration,
      AVG(duration_seconds) as avg_call_duration
    FROM MYDB.MYSCHEMA.CALLS
    GROUP BY agent_id
    ORDER BY total_calls DESC
    LIMIT 10;
    
  5. Request approval flow

    How to interpret user instructions:

    • If instruction says "Request approvals are handled inside Snowflake" → include request_approval_type: "REQUEST_AND_APPROVE_IN_SNOWFLAKE"
    • If instruction says "Request approvals are handled outside Snowflake" → include request_approval_type: "REQUEST_AND_APPROVE_OUTSIDE_SNOWFLAKE"
    • If user does not specify how approvals are handled → omit the field (defaults to REQUEST_AND_APPROVE_OUTSIDE_SNOWFLAKE)

    Example when approvals are handled inside Snowflake:

    request_approval_type: "REQUEST_AND_APPROVE_IN_SNOWFLAKE"
    
  6. Auto-fulfillment:

    ⚠️ When auto-fulfillment is REQUIRED (include it when ANY of these apply):

    • Targeting an account in a different region than the current account, OR
    • Targeting all accounts in the organization (which may include accounts in different regions), OR
    • Using a remote access region (an access region different from the current account's region)

    ⚠️ When auto-fulfillment is NOT required (omit it):

    • Targeting only the current account (same region)
    • Targeting only accounts in the same region as the current account AND using only that region as access region

    Add auto-fulfillment setting to the manifest in this format ONLY when required:

    auto_fulfillment:
       refresh_type: "SUB_DATABASE"
       refresh_schedule: "10 MINUTE"  # Check existing listings on same DB for schedule
    
  7. Generate the manifest and present to user:

CREATE ORGANIZATION LISTING <listing_name>
  SHARE <share_name> AS
$$
title: "<Listing Title - max 110 chars>"
description: |
     <Detailed description - supports Markdown>

organization_profile: "<Organization Profile Name>"

organization_targets:
  discovery:
    # Targeting all accounts in the organization
    - all_internal_accounts: true
    # OR for specific accounts (use singular "account:" NOT "accounts:"):
    # - account: "ACCOUNT_1"
    # - account: "ACCOUNT_2"
    # OR omit discovery field entirely if user says "Do not allow discovery"
  access:
    # Targeting all accounts in the organization
    - all_internal_accounts: true
    # OR for specific accounts:
    # - account: "ACCOUNT_1"
    # - account: "ACCOUNT_2"
    # ⚠️ Use ONLY account_name from SHOW ACCOUNTS

support_contact: "<support_email>"  # Use user's support contact email
approver_contact: "<approver_email>"  # Use user's approver contact email, always include

# Include request_approval_type if user specifies approval handling method
# Examples:
# request_approval_type: "REQUEST_AND_APPROVE_IN_SNOWFLAKE"  # When "handled inside Snowflake"
# request_approval_type: "REQUEST_AND_APPROVE_OUTSIDE_SNOWFLAKE"  # When "handled outside Snowflake"
# Omit if not specified (defaults to REQUEST_AND_APPROVE_OUTSIDE_SNOWFLAKE)

# Always include data_dictionary for discoverability (up to 5 objects)
# ⚠️ Use UNQUOTED identifiers for database, schema, and object names
data_dictionary:
  featured:
    database: DATABASE_NAME  # No quotes!
    objects:
      - schema: SCHEMA_NAME  # No quotes!
        name: TABLE_1  # No quotes!
        domain: TABLE
      - schema: SCHEMA_NAME
        name: TABLE_2
        domain: TABLE
      # Auto-select up to 5 most relevant tables/views

   # Always include usage_examples to help consumers (2-3 examples)
   # ⚠️ ALWAYS use fully qualified table names: DATABASE.SCHEMA.TABLE
usage_examples:
  - title: "<Example Title - max 110 chars>"
    description: "<What this query demonstrates - max 300 chars>"
    query: |
      SELECT col1, col2 FROM DATABASE.SCHEMA.TABLE WHERE condition
  - title: "<Second Example>"
    description: "<Description>"
    query: |
      SELECT * FROM DATABASE.SCHEMA.TABLE LIMIT 10

   # Access regions: Can be ALL, specific regions, or current region based on instruction
   # Examples:
   #   - "all regions" instruction → ALL
   #   - "specific regions" → PUBLIC.AWS_US_WEST_2, PUBLIC.AWS_US_EAST_1
   #   - No mention → current region (e.g., PUBLIC.AWS_US_WEST_2)
locations:
  access_regions:
    - name: "PUBLIC.AWS_US_WEST_2"  # Default: current region if not specified
    
# Include auto_fulfillment ONLY when:
# - Targeting accounts in different region, OR
# - Targeting all accounts, OR  
# - Using remote access region (different from current region)
# auto_fulfillment:
#   refresh_type: "SUB_DATABASE"
#   refresh_schedule: "10 MINUTE"  # Check existing listings on same DB for schedule
$$ PUBLISH = <FALSE if user explicitly specified to create a draft listing or not to publish the listing, otherwise TRUE>;

⚠️ CRITICAL - PUBLISH flag:

  • If instruction says "Create a draft listing" → use PUBLISH = FALSE
  • If instruction says "Create and publish" or just "Create" → use PUBLISH = TRUE
  • Default to TRUE unless explicitly told to create a draft

⚠️ CRITICAL - Auto-fulfillment: Include auto_fulfillment when ANY of these apply:

  • Targeting accounts in different region than current account
  • Targeting all accounts in the organization
  • Using remote access region (different from current account's region)

⚠️ Refresh Schedule: If other listings exist on the same database, the refresh_schedule MUST match. Query existing listings to check.

⚠️ CRITICAL: Do NOT use CREATE LISTING syntax to create organizational listing

⚠️ MANDATORY STOPPING POINT: Present complete manifest to user for confirmation before executing.

Show summary:

Summary:
- Share name: <share_name>
- Objects included: <list of tables/views>
- Featured in data dictionary: <up to 5 key tables/views>
- PII detected: <Yes/No - list fields if Yes>
- Usage examples: <number of examples generated>
- Discovery: <all accounts / specific accounts>
- Access: <all accounts / specific accounts with roles>
- Regions: ALL (default)

Does this look correct? (Yes/No)

Only execute after user confirms.


Step 4: Verify and Notify

Goal: Confirm listing created and provide user with access information.

Actions:

  1. Verify listing:

    SHOW LISTINGS;
    DESCRIBE LISTING <listing_name>;
    
  2. Notify user (always show listing TITLE, not internal name):

    • To get the listing global name, run:
    DESCRIBE LISTING <listing_name>
    

    and use the exact name from the 'global_name' column.

    ✅ Your data product "<LISTING_TITLE>" has been created successfully!
    
    **Listing Title:** <listing_title>  ← Always show title to user
    **Share Name:** <share_name>
    **State:** PUBLISHED (automatic for org listings)
    **Listing URL:** https://app.snowflake.com/marketplace/internal/listing/<listing_global_name>
    
    **To view your listing:**
    1. Go to Snowsight: https://app.snowflake.com
    2. Navigate: Data Sharing → Internal Sharing → Listings tab
    3. Find your listing: "<listing_title>"
    

    ⚠️ Always display the listing TITLE (e.g., "Customer Analytics Data"), not the internal listing name (e.g., CUSTOMER_ANALYTICS_LISTING)

Output: Published organizational listing accessible to target accounts.


Step 5: Manage Listing (Optional)

If user wants to update the listing:

Add objects to share:

GRANT SELECT ON TABLE <db>.<schema>.<new_table> TO SHARE <share_name>;
DESCRIBE SHARE <share_name>;

Update manifest:

-- ⚠️ NOTE: Use "AS" without "SET" when updating manifest content
-- ⚠️ NOTE: "CREATE OR REPLACE" is NOT supported for org listings - use ALTER
ALTER LISTING <listing_name> AS $$
title: "Updated Title"
-- ... updated manifest fields
$$;

Publish listing (if not auto-published):

-- ⚠️ Use ALTER LISTING ... PUBLISH (not SET STATE = PUBLISHED)
ALTER LISTING <listing_name> PUBLISH;

Unpublish listing:

ALTER LISTING <listing_name> UNPUBLISH;

Delete listing:

DROP LISTING <listing_name>;
DROP SHARE <share_name>;  -- Optional

Handle access requests (if using REQUEST_AND_APPROVE_IN_SNOWFLAKE):

-- View pending requests
SELECT * FROM SNOWFLAKE.DATA_SHARING_USAGE.LISTING_ACCESS_REQUESTS
WHERE LISTING_NAME = '<listing_name>' AND REQUEST_STATUS = 'PENDING';

-- Approve/deny
CALL SYSTEM$APPROVE_LISTING_REQUEST('<request_id>');
CALL SYSTEM$DENY_LISTING_REQUEST('<request_id>', 'Reason');

Organization Targets Quick Reference

⚠️ SYNTAX WARNING: Use singular account: NOT plural accounts:

All accounts discover & access:

organization_targets:
  discovery:
    - all_internal_accounts: true
  access:
    - all_internal_accounts: true

Specific accounts for discovery AND access:

# ⚠️ Use singular "account:" - NOT "accounts:"
organization_targets:
  discovery:
    - account: "ACCOUNT_1"  # ← singular "account:"
    - account: "ACCOUNT_2"
  access:
    - account: "ACCOUNT_1"
    - account: "ACCOUNT_2"

Specific accounts with roles:

organization_targets:
  discovery:
    - all_internal_accounts: true
  access:
    - account: 'finance_account'  # ← singular "account:"
      roles: ['analyst', 'manager']
    - account: 'analytics_account'

Stopping Points

  • ✋ Step 1: After gathering requirements (confirm all inputs before proceeding)
  • ✋ Step 3: After generating manifest (confirm YAML before execution)

Resume rule: Upon user approval, proceed directly to next step without re-asking.

Output

  • Published organizational listing in Internal Marketplace
  • Share containing specified database objects
  • Snowsight URL for listing management
  • ULL (Uniform Listing Locator) for referencing the listing

Common Errors Quick Reference

ErrorCauseFix
"Share does not currently have a database"Wrong grant orderGrant DATABASE first, then SCHEMA, then TABLES
"invalid identifier 'column_name'"Wrong column name in usage_examplesRun DESCRIBE TABLE first, use actual column names
YAML syntax error with accounts:Used pluralUse singular account: not accounts:
"Semantic view not found"Used SHOW VIEWSUse SHOW SEMANTIC VIEWS instead
"USAGE not supported for semantic view"Wrong privilegeUse GRANT SELECT ON SEMANTIC VIEW
"Missing approver contact"Field requiredAlways include approver_contact

References

For detailed information, load these files:

  • references/manifest-reference.md: All manifest fields, data attributes, data dictionary config, access control setup
  • references/templates.md: Quick copy-paste templates for common scenarios
  • references/errors.md: Common errors and troubleshooting guide

Known Limitations

  • Each share can be attached to one listing only
  • Reader accounts not supported with organizational listings
  • Native App listings don't support target roles
  • Multiple regions require auto-fulfillment configuration
  • Provider studio analytics not supported for org listings

Installation

Install using the Cortex CLI:

bash
$cortex skill install data-products

Requires Cortex CLI v2.0+. Install guide →

Repository Sources

Loading repository files...

SKILL.md Preview

SKILL.md
---
name: internal-marketplace-org-listing
description: >
  Create organizational listings to share data products via Internal Marketplace.
  Triggers: create data product, share to internal marketplace, publish to internal marketplace,
  share to other accounts, share with other accounts, organization listing, org listing,
  share across accounts, internal marketplace, cross-account sharing, share my agent to other accounts.
  
  WHEN TO USE THIS SKILL:
  - User wants to share with OTHER ACCOUNTS → Use this skill
  - User mentions "internal marketplace" or "data product" (even for same account) → Use this skill
  
  WHEN TO USE RBAC INSTEAD (not this skill):
  - User wants to share with roles in SAME account only
  - User does NOT mention "internal marketplace" or "data product" or "listing"
  - Example: "share this table with ANALYST role" → Use GRANT, not this skill
  
  KEY: If user says "share via internal marketplace" or "as a data product" even for
  same-account roles, use this skill. Otherwise, same-account = regular RBAC grants.
---

# Organizational Listing Provider Skill

Create and publish organizational listings to share data products across accounts within your Snowflake organization via Internal Marketplace.

## When to Use

**USE THIS SKILL when:**
- Sharing objects with **OTHER ACCOUNTS** in the organization
- User mentions **"internal marketplace"** or **"data product"** (even for same account)
- Creating internal marketplace listings as a data provider
- Publishing data products to internal consumers
- Cross-region auto-fulfillment setup

**USE RBAC (not this skill) when:**
- User wants to share with roles in the **SAME account only**
- User does NOT mention "internal marketplace" or "data product"
- Example: "grant access to ANALYST role" → Use `GRANT` command, not this skill

**Common triggers**: "share to internal marketplace", "create a data product", "share with other accounts", "publish to internal marketplace"

**Documentation**: [Organization Listing Docs](https://docs.snowflake.com/en/user-guide/collaboration/listings/organizational/org-listing-create)

## Quick Flow (Minimal Input)

When user says something like **"share my agent to internal marketplace"** or **"share this object internally"**:

1. **Identify the object(s)** the user wants to share
2. **Ask for required info**:
   ```
   To create the listing, I need:
   1. Who should have access? (all accounts / specific accounts)
   2. What email should I use for support and approver contacts?
   ```
3. **Auto-generate** listing with minimal fields:
   - Title: create a meaningful title that describes the data product and all objects included
   - Description: auto-generated a helpful description on what this data product can do, what objects it includes, and what use cases it can help address
   - Discovery/Access: based on user's input
   - Contacts: use the email provided by the user for both support and approver if only one email is provided, otherwise use the email to the specific contact field specified by the user.
   - Request approval flow: Include `request_approval_type` if user explicitly specifies how approvals should be handled
   - Data Dictionary: Add data dictionary for all tables and views added in the data product
4. **Skip data dictionary** for non-table objects (agents, semantic views, functions)
5. **Create and publish** immediately after confirmation

**⚠️ Note**: Data dictionary and usage examples are **only applicable for tables/views**. Skip these for agents, semantic views, functions, and other non-queryable objects.

**⚠️ Cortex Agent Sharing Limitations**: Cortex Agents CANNOT be shared if they:
- Use a custom warehouse in agent spec or tools
- Have tools in different databases
- Have custom `query_timeout` settings
- Have an invalid agent spec

If agent sharing fails, suggest sharing the underlying tables instead.

## Prerequisites

1. **Organization Setup**:
   - Account must be part of a Snowflake organization with `ORGADMIN` role
   - Know your organization's account names (`SHOW ORGANIZATION ACCOUNTS`)

2. **Required Privileges**:
   - `CREATE SHARE` on ACCOUNT
   - `CREATE ORGANIZATION LISTING` on ACCOUNT
   - `USAGE WITH GRANT OPTION` on database/schema to share
   - `MANAGE LISTING AUTO FULFILLMENT` on ACCOUNT (if cross-region)

**Verify with:**
```sql
SELECT CURRENT_ROLE();
SHOW GRANTS TO ROLE <your_role>;
```

## Workflow

```
Start → Step 1: Gather → Step 2: Create Share → Step 3: Create Listing → Step 4: Verify → Done
            ↑                                         ↑
      ⚠️ STOP                                   ⚠️ STOP
```

### Step 1: Gather Requirements

**Goal:** Collect all information needed to create the share and listing.

**Actions:**

1. **Ask** the user:
   ```
   To create your organizational listing, please provide:
   
   1. **Objects to share**: Which database/schema/tables/views/semantic views?
      (Please list the EXACT objects - only these will be added to the share)
   2. **Access**: Who should have access?
      - All internal accounts (or user already said "share with all accounts")
      - Specific accounts only (please list them)
   3. **Contact email**: What email should be used for support and approver contacts?
   4. **Organization profile** Which organization profile should be used for this listing? 
      - The system-generated default INTERNAL profile
      - An available custom profile in your organization (please specify the name)
   ```

2. **Auto-generate** (do not ask user for these):
   - **Title**: Create a meaningful title that describes the data product and all objects included
   - **Description**: Generate a helpful description explaining what this data product offers, what objects it includes, and what use cases it can address
   - **Data Dictionary**: Add data dictionary for all tables and views in the data product
   - **Support & Approver contacts**: 
      - If the user provides TWO distinct emails with labels (e.g., "Support Contact: email1" and "Approver Contact: email2"), map each email to its corresponding field (support_contact = email1, approver_contact = email2)
      - If the user provides only ONE email, use it for BOTH support_contact and approver_contact fields

   **⚠️ CRITICAL**: Only share objects the user explicitly lists. Never add:
   - INFORMATION_SCHEMA
   - System schemas or tables
   - Objects not explicitly requested by the user

3. **If user asks to share "all objects in a schema"**, discover them:
   ```sql
   -- Get all tables
   SHOW TABLES IN SCHEMA <database>.<schema>;
   
   -- Get all views
   SHOW VIEWS IN SCHEMA <database>.<schema>;
   
   -- Get all semantic views (NOT included in SHOW VIEWS)
   SHOW SEMANTIC VIEWS IN SCHEMA <database>.<schema>;
   ```
   Compile the list from all three commands, then confirm with user before proceeding.

4. **If user mentions sharing to accounts or targeting accounts, or if user mentions targeting/sharing to regions**

   **Step A**: Verify whether a specified account is a valid account in the organization:
   Fetch all the accounts in the organization by running:
   ```sql
   SHOW ACCOUNTS;
   -- record query id
   ```
   **⚠️ CRITICAL**: DO NOT run `SHOW ORGANIZATION ACCOUNTS` to fetch the accounts.
   **⚠️ CRITICAL**: `SHOW ACCOUNTS` output may be truncated in large organizations. If the result indicates truncation (e.g., "N row(s) shown but more may have been returned"), always use `RESULT_SCAN` with a WHERE filter to search for specific accounts in the response of `SHOW ACCOUNTS` rather than scanning the raw output visually:
    ```sql
   -- use the returned `account_name` and `snowflake_region` for the following steps.
   SELECT "account_name", "account_locator", "snowflake_region"
   FROM TABLE(RESULT_SCAN(<query id of show accounts>))
   WHERE UPPER("account_name") = '<ACCOUNT>' OR UPPER("account_locator") = '<ACCOUNT>';
    ```

   If the user specified the account name in the format of "<organization name>.<account alias>", use the account alias as the account name for account name verification and following steps if the organization name is the same as the current account. ALWAYS use `account_name` instead of `account_locator` in the following steps even when the user specified the account locator.
   
   **Step B**: Use the exact account name in `organization_targets`:
   
   **⚠️ CRITICAL - Account Name Format:**
   - Use ONLY the `account_name` from `SHOW ACCOUNTS`
   - **NEVER** append region names
   - **NEVER** use account locators
   
   ```yaml
   organization_targets:
     discovery:
       - account: "HR"  # Use exact account_name from SHOW ACCOUNTS
     access:
       - account: "HR"  
   ```
   
   **⚠️ OPTIMIZATION - For "current account" or "same account":**
   
   When instruction mentions "**current account**", "**same account**", or "roles in this account", you can use `CURRENT_ACCOUNT_NAME()` directly without needing to query `SHOW ACCOUNTS`:
   
   ```sql
   SELECT CURRENT_ACCOUNT_NAME() as account_name;
   ```
   
   Use the returned value directly in `organization_targets`:
   
   ```yaml
   organization_targets:
     access:
       - account: "PM_AWS_US_WEST_2"  # From CURRENT_ACCOUNT_NAME()
         roles: ['ACCOUNTADMIN', 'SYSADMIN']
   ```

5. **Verify the specified organization profile**:
   
   Find all the organization profiles available in this organization:
   ```sql
   SHOW AVAILABLE ORGANIZATION PROFILES;
   -- Convert the user-specified organization profile name to all uppercase if needed and look for the exact organization profile name from the 'name' column.
   ```
   **⚠️ CRITICAL**: An organization profile is only available for publishing listings when the exact name matches with the uppercase format of the user-specified name, and the 'can_publish_listings_with_profile' column for this organization profile is true.

   If the specified organization profile is not found, list the names of the available organization profiles with the 'can_publish_listings_with_profile' column as true and ask the user to choose from one of these options.
   

6. **Get current region** (needed for locations):
   ```sql
   SELECT CURRENT_REGION();
   ```

**⚠️ MANDATORY STOPPING POINT**: Do NOT proceed until user provides all required information.

---

### Step 2: Create the Share

**Goal:** Create the underlying share with correct privilege grants.

```
╔══════════════════════════════════════════════════════════════════════════════╗
║  ⚠️ CRITICAL: GRANT ORDER MATTERS - FOLLOW EXACTLY OR SHARE WILL FAIL       ║
║                                                                              ║
║  1. FIRST:  GRANT USAGE ON DATABASE  ← Must be first!                        ║
║  2. SECOND: GRANT USAGE ON SCHEMA                                            ║
║  3. LAST:   GRANT SELECT ON TABLE/VIEW/SEMANTIC VIEW                         ║
║                                                                              ║
║  Error "Share does not currently have a database" = Wrong order!             ║
╚══════════════════════════════════════════════════════════════════════════════╝
```

**⚠️ CRITICAL**: Only add objects the user explicitly specifies to the share. 
- Do NOT add INFORMATION_SCHEMA
- Do NOT add system schemas
- Do NOT add objects the user didn't request
- Ask user to confirm the exact list of objects before creating the share

**Actions:**

1. **Create share**:
   ```sql
   CREATE SHARE IF NOT EXISTS <share_name>
     COMMENT = '<description of what is being shared>';
   ```

2. **Grant privileges** (in order!):
   ```sql
   -- FIRST: Database
   GRANT USAGE ON DATABASE <database_name> TO SHARE <share_name>;
   
   -- SECOND: Schema
   GRANT USAGE ON SCHEMA <database_name>.<schema_name> TO SHARE <share_name>;

   -- LAST: Tables/Views/Semantic Views
   -- For tables:
   GRANT SELECT ON TABLE <database_name>.<schema_name>.<table> TO SHARE <share_name>;
   -- Or for all tables:
   GRANT SELECT ON ALL TABLES IN SCHEMA <database_name>.<schema_name> TO SHARE <share_name>;
   
   -- ⚠️ VIEWS: Must grant individually (bulk grant on views is restricted)
   GRANT SELECT ON VIEW <database_name>.<schema_name>.<view> TO SHARE <share_name>;
   -- NOTE: "GRANT SELECT ON ALL VIEWS" is NOT supported for shares
   
   -- ⚠️ SEMANTIC VIEWS: Use SELECT (not USAGE)
   GRANT SELECT ON SEMANTIC VIEW <database_name>.<schema_name>.<semantic_view> TO SHARE <share_name>;
   ```
   
   **⚠️ Finding Semantic Views**: Use `SHOW SEMANTIC VIEWS` (not `SHOW VIEWS`):
   ```sql
   SHOW SEMANTIC VIEWS IN SCHEMA <database_name>.<schema_name>;
   ```

   **If error "Non-secure object can only be granted to shares with "secure_objects_only" property set to false." happens when granting any of the tables, views, or functions to the share** → List all the options and ask the user to confirm how they want to proceed with the share creation:
   - Option 1: Alter the share to allow sharing non-secure objects. Show a bold warning with this option that a share cannot set secure_objects_only to true once it's set to false, execute
   ```sql
   ALTER SHARE <share_name> SET SECURE_OBJECTS_ONLY = FALSE;
   ```
   - Option 2: Convert this object to a secure object. Show a bold warning with this option that users should weigh the trade-off between data privacy/security and query performance before proceeding. If the user chooses option 2, execute: 
   ```sql
   ALTER VIEW <database_name>.<schema_name>.<view> SET SECURE;
   ```
   - Option 3: Skip granting this non-secure object to the share. 

3. **Verify share contents**:
   ```sql
   DESCRIBE SHARE <share_name>;
   ```

**Output:** Share created with all requested objects granted.

**If error "Share does not currently have a database"** → Check grant order (database must be first).

**⚠️ Metadata Visibility Note**: Granting `USAGE ON DATABASE` makes all schema names visible to consumers in metadata, even if they can't query objects in those schemas.

---

### Step 3: Create the Listing

**Goal:** Create organizational listing with YAML manifest including data dictionary.

**Actions:**

0. **Organization Targets - Discovery & Access**:
   
   **⚠️ CRITICAL - How to handle discovery targets:**
   
   - If instruction says "**Do not** allow anyone to discover" or "**No discovery**" → **OMIT the `discovery` field entirely** from `organization_targets`:
     ```yaml
     organization_targets:
       access:
         - account: "ACCOUNT_NAME"
       # NO discovery field when discovery is disabled
     ```
   
   - If instruction says "**all accounts** in the organization" for discovery or access → use `all_internal_accounts: true`:
     ```yaml
     organization_targets:
       discovery:
         - all_internal_accounts: true
       access:
         - all_internal_accounts: true
     ```
   
   - If instruction specifies specific accounts for discovery or access → list them:
     ```yaml
     organization_targets:
       discovery:
         - account: "ACCOUNT_1"
       access:
         - account: "ACCOUNT_1"
     ```
   
   **⚠️ CRITICAL - Role-based access:**
   - If instruction mentions roles (e.g., "ACCOUNTADMIN role in PM_SHARING" or "ACCOUNTADMIN and SYSADMIN roles") → include `roles` field in the following format:
     ```yaml
     organization_targets:
       access:
         - account: "PM_SHARING"
           roles: ['ACCOUNTADMIN', 'SYSADMIN']
     ```

1. **Access Regions**:
   
   **⚠️ The listing owner can specify any access regions they want. This is independent of auto-fulfillment.**
   
   **How to choose access regions:**
   
   - **If instruction explicitly says "all regions" or "target all regions":**
     - Use the literal value `ALL`:

   
   - **If instruction specifies specific regions:**
     - Use those specific regions (e.g., `PUBLIC.AWS_US_WEST_2`, `PUBLIC.AWS_US_EAST_1`)
   
   - **If instruction doesn't specify regions:**
     - Default to the current account's region (e.g., `PUBLIC.AWS_US_WEST_2`)
   
   **Note:** Access region choice does NOT determine auto-fulfillment. See section 5 below for auto-fulfillment logic.

   When using specific access regions:
   - If user requests targeting specific regions, or mentions targeting only the target accounts' regions or locations, or mentions targeting the minimal set of regions possible, add the access region names to the manifest.

      Each access region name should be in the format of "<region_group>.<snowflake_region>", e.g., "PUBLIC.AWS_US_WEST_2". If the user specified a set of regions, use the specified region list; otherwise add the regions of all targeted accounts without duplication. If the region group is not specified for any snowflake regions, run:
      ```sql
      SHOW REGIONS IN DATA EXCHANGE SNOWFLAKE_DATA_MARKETPLACE;
      ```
      and use the values in the `region_group` field in the response for the corresponding `snowflake_regions` of the accounts. 

      If any of the target accounts are outside the access regions, list the following options and ask user for supplemental information:
         - Skip this target account that is not in any of the specified access regions.
         - Add the access region "<region_group>.<snowflake_region>" to the access regions

   Add the access regions to the manifest in the following format:
   ```yaml
      locations:
      access_regions:
         - name: "<REGION_NAME>"
         - name: "<REGION_NAME_2>"
   ```

2. **Auto-select tables for data dictionary** (up to 5):
   
   **⚠️ SKIP this step if sharing non-table objects** (agents, semantic views, functions). Data dictionary is only supported for tables and views.
   
   - Query the share to identify objects:
     ```sql
     DESCRIBE SHARE <share_name>;
     ```
   - **Prioritize** (select up to 5 most relevant):
     - Main fact tables (transactions, events, orders)
     - Key dimension tables (customers, products)
     - Commonly queried views
     - Aggregated/summary tables
   - **Exclude**: staging tables, internal/system tables, rarely used lookups
   
   - **Auto-detect PII fields** in selected objects (tables only):
     ```sql
     -- Check column names for PII patterns
     DESCRIBE TABLE <database>.<schema>.<table>;
     
     -- If available, check Snowflake classification tags
     SELECT * FROM TABLE(
       INFORMATION_SCHEMA.TAG_REFERENCES('<database>.<schema>.<table>', 'TABLE')
     );
     ```
   - **Common PII patterns to detect**:
     - Names: `first_name`, `last_name`, `full_name`, `customer_name`
     - Contact: `email`, `phone`, `mobile`, `address`, `zip`, `postal`
     - IDs: `ssn`, `social_security`, `tax_id`, `passport`, `driver_license`
     - Financial: `credit_card`, `account_number`, `bank_account`
     - Health: `dob`, `date_of_birth`, `medical_id`, `patient_id`
   - **Note PII fields in description** for consumer awareness

3. **Auto-generate SQL usage examples** (tables/views only):
   
   **⚠️ SKIP if no tables/views in the data product** (e.g., only agents or functions).
   
   ```
   ╔══════════════════════════════════════════════════════════════════════════╗
   ║  ⚠️ MANDATORY: Run DESCRIBE TABLE for EACH table BEFORE writing queries ║
   ║                                                                          ║
   ║  NEVER assume column names! Get the ACTUAL column names first.           ║
   ╚══════════════════════════════════════════════════════════════════════════╝
   ```
   
   **Step 0: Get ACTUAL column names (MANDATORY)**
   ```sql
   -- Run this for EACH table before writing any usage examples
   DESCRIBE TABLE <database>.<schema>.<table>;
   ```
   Use ONLY the column names returned by DESCRIBE. Never guess or assume.
   
   **Step 2: Think about what questions users would ask this data**
   
   Based on the table/column names, deduce what the data represents and what insights users would want:
   
   | Data Type | Example Tables | Questions Users Would Ask | Query Pattern |
   |-----------|----------------|---------------------------|---------------|
   | **Sales/Orders** | orders, transactions, sales | "What's the revenue by region?" "Top customers?" | GROUP BY with SUM, ranking |
   | **Customer/User** | customers, users, accounts | "How many active users?" "Customer segments?" | COUNT, segmentation, cohorts |
   | **Events/Logs** | events, logs, activity | "What happened last 7 days?" "Error rate?" | Time filters, COUNT by type |
   | **Product/Inventory** | products, inventory, catalog | "What's in stock?" "Top products?" | JOINs, availability checks |
   | **Financial** | invoices, payments, budgets | "Monthly spend?" "Outstanding balance?" | SUM, date aggregations |
   
   **Step 3: Generate 2-3 meaningful queries**
   
   **Rules:**
   - **ALWAYS use fully qualified table names**: `DATABASE.SCHEMA.TABLE`
   - **NEVER use `SELECT *`** - select specific, useful columns
   - **Include aggregations** (SUM, COUNT, AVG) with GROUP BY
   - **Include JOINs** if multiple related tables exist
   - **Include date filters** for time-series data
   - Validate SQL compiles before adding to manifest
   
   **Example - For a CALLS table with columns (call_id, agent_id, duration_seconds, created_at, status):**
   ```sql
   -- Example 1: Call volume and average duration by day
   SELECT 
     DATE_TRUNC('day', created_at) as call_date,
     COUNT(*) as total_calls,
     AVG(duration_seconds) as avg_duration_sec,
     SUM(CASE WHEN status = 'completed' THEN 1 ELSE 0 END) as completed_calls
   FROM MYDB.MYSCHEMA.CALLS
   WHERE created_at >= DATEADD('day', -30, CURRENT_DATE)
   GROUP BY 1
   ORDER BY 1 DESC;
   
   -- Example 2: Top agents by call volume
   SELECT 
     agent_id,
     COUNT(*) as total_calls,
     SUM(duration_seconds) as total_duration,
     AVG(duration_seconds) as avg_call_duration
   FROM MYDB.MYSCHEMA.CALLS
   GROUP BY agent_id
   ORDER BY total_calls DESC
   LIMIT 10;
   ```

4. **Request approval flow**
   
   **How to interpret user instructions:**
   - If instruction says "Request approvals are handled inside Snowflake" → **include** `request_approval_type: "REQUEST_AND_APPROVE_IN_SNOWFLAKE"`
   - If instruction says "Request approvals are handled outside Snowflake" → **include** `request_approval_type: "REQUEST_AND_APPROVE_OUTSIDE_SNOWFLAKE"`
   - If user does not specify how approvals are handled → **omit** the field (defaults to `REQUEST_AND_APPROVE_OUTSIDE_SNOWFLAKE`)

   Example when approvals are handled inside Snowflake:
   ```yaml
   request_approval_type: "REQUEST_AND_APPROVE_IN_SNOWFLAKE"
   ```

5. **Auto-fulfillment**:
   
   **⚠️ When auto-fulfillment is REQUIRED (include it when ANY of these apply):**
   - Targeting an account in a different region than the current account, OR
   - Targeting all accounts in the organization (which may include accounts in different regions), OR
   - Using a remote access region (an access region different from the current account's region)
   
   **⚠️ When auto-fulfillment is NOT required (omit it):**
   - Targeting only the current account (same region)
   - Targeting only accounts in the same region as the current account AND using only that region as access region
   
   Add auto-fulfillment setting to the manifest in this format **ONLY when required**:
   ```yaml
   auto_fulfillment:
      refresh_type: "SUB_DATABASE"
      refresh_schedule: "10 MINUTE"  # Check existing listings on same DB for schedule
   ```

6. **Generate the manifest** and present to user:

```sql
CREATE ORGANIZATION LISTING <listing_name>
  SHARE <share_name> AS
$$
title: "<Listing Title - max 110 chars>"
description: |
     <Detailed description - supports Markdown>

organization_profile: "<Organization Profile Name>"

organization_targets:
  discovery:
    # Targeting all accounts in the organization
    - all_internal_accounts: true
    # OR for specific accounts (use singular "account:" NOT "accounts:"):
    # - account: "ACCOUNT_1"
    # - account: "ACCOUNT_2"
    # OR omit discovery field entirely if user says "Do not allow discovery"
  access:
    # Targeting all accounts in the organization
    - all_internal_accounts: true
    # OR for specific accounts:
    # - account: "ACCOUNT_1"
    # - account: "ACCOUNT_2"
    # ⚠️ Use ONLY account_name from SHOW ACCOUNTS

support_contact: "<support_email>"  # Use user's support contact email
approver_contact: "<approver_email>"  # Use user's approver contact email, always include

# Include request_approval_type if user specifies approval handling method
# Examples:
# request_approval_type: "REQUEST_AND_APPROVE_IN_SNOWFLAKE"  # When "handled inside Snowflake"
# request_approval_type: "REQUEST_AND_APPROVE_OUTSIDE_SNOWFLAKE"  # When "handled outside Snowflake"
# Omit if not specified (defaults to REQUEST_AND_APPROVE_OUTSIDE_SNOWFLAKE)

# Always include data_dictionary for discoverability (up to 5 objects)
# ⚠️ Use UNQUOTED identifiers for database, schema, and object names
data_dictionary:
  featured:
    database: DATABASE_NAME  # No quotes!
    objects:
      - schema: SCHEMA_NAME  # No quotes!
        name: TABLE_1  # No quotes!
        domain: TABLE
      - schema: SCHEMA_NAME
        name: TABLE_2
        domain: TABLE
      # Auto-select up to 5 most relevant tables/views

   # Always include usage_examples to help consumers (2-3 examples)
   # ⚠️ ALWAYS use fully qualified table names: DATABASE.SCHEMA.TABLE
usage_examples:
  - title: "<Example Title - max 110 chars>"
    description: "<What this query demonstrates - max 300 chars>"
    query: |
      SELECT col1, col2 FROM DATABASE.SCHEMA.TABLE WHERE condition
  - title: "<Second Example>"
    description: "<Description>"
    query: |
      SELECT * FROM DATABASE.SCHEMA.TABLE LIMIT 10

   # Access regions: Can be ALL, specific regions, or current region based on instruction
   # Examples:
   #   - "all regions" instruction → ALL
   #   - "specific regions" → PUBLIC.AWS_US_WEST_2, PUBLIC.AWS_US_EAST_1
   #   - No mention → current region (e.g., PUBLIC.AWS_US_WEST_2)
locations:
  access_regions:
    - name: "PUBLIC.AWS_US_WEST_2"  # Default: current region if not specified
    
# Include auto_fulfillment ONLY when:
# - Targeting accounts in different region, OR
# - Targeting all accounts, OR  
# - Using remote access region (different from current region)
# auto_fulfillment:
#   refresh_type: "SUB_DATABASE"
#   refresh_schedule: "10 MINUTE"  # Check existing listings on same DB for schedule
$$ PUBLISH = <FALSE if user explicitly specified to create a draft listing or not to publish the listing, otherwise TRUE>;
```

   **⚠️ CRITICAL - PUBLISH flag:**
   - If instruction says "Create a **draft** listing" → use `PUBLISH = FALSE`
   - If instruction says "Create and **publish**" or just "Create" → use `PUBLISH = TRUE`
   - Default to `TRUE` unless explicitly told to create a draft

   **⚠️ CRITICAL - Auto-fulfillment:**
   Include `auto_fulfillment` when ANY of these apply:
   - Targeting accounts in different region than current account
   - Targeting all accounts in the organization
   - Using remote access region (different from current account's region)
   
   **⚠️ Refresh Schedule**: If other listings exist on the same database, the refresh_schedule MUST match. Query existing listings to check.

   **⚠️ CRITICAL**: Do NOT use CREATE LISTING syntax to create organizational listing

**⚠️ MANDATORY STOPPING POINT**: Present complete manifest to user for confirmation before executing.

Show summary:
```
Summary:
- Share name: <share_name>
- Objects included: <list of tables/views>
- Featured in data dictionary: <up to 5 key tables/views>
- PII detected: <Yes/No - list fields if Yes>
- Usage examples: <number of examples generated>
- Discovery: <all accounts / specific accounts>
- Access: <all accounts / specific accounts with roles>
- Regions: ALL (default)

Does this look correct? (Yes/No)
```

**Only execute after user confirms.**

---

### Step 4: Verify and Notify

**Goal:** Confirm listing created and provide user with access information.

**Actions:**

1. **Verify listing**:
   ```sql
   SHOW LISTINGS;
   DESCRIBE LISTING <listing_name>;
   ```

2. **Notify user** (always show listing TITLE, not internal name):
   - To get the listing global name, run:
   ```sql
   DESCRIBE LISTING <listing_name>
   ```
   and use the exact name from the 'global_name' column. 

   ```
   ✅ Your data product "<LISTING_TITLE>" has been created successfully!
   
   **Listing Title:** <listing_title>  ← Always show title to user
   **Share Name:** <share_name>
   **State:** PUBLISHED (automatic for org listings)
   **Listing URL:** https://app.snowflake.com/marketplace/internal/listing/<listing_global_name>
   
   **To view your listing:**
   1. Go to Snowsight: https://app.snowflake.com
   2. Navigate: Data Sharing → Internal Sharing → Listings tab
   3. Find your listing: "<listing_title>"
   ```
   
   **⚠️ Always display the listing TITLE** (e.g., "Customer Analytics Data"), not the internal listing name (e.g., CUSTOMER_ANALYTICS_LISTING)

**Output:** Published organizational listing accessible to target accounts.

---

### Step 5: Manage Listing (Optional)

**If user wants to update the listing:**

**Add objects to share:**
```sql
GRANT SELECT ON TABLE <db>.<schema>.<new_table> TO SHARE <share_name>;
DESCRIBE SHARE <share_name>;
```

**Update manifest:**
```sql
-- ⚠️ NOTE: Use "AS" without "SET" when updating manifest content
-- ⚠️ NOTE: "CREATE OR REPLACE" is NOT supported for org listings - use ALTER
ALTER LISTING <listing_name> AS $$
title: "Updated Title"
-- ... updated manifest fields
$$;
```

**Publish listing** (if not auto-published):
```sql
-- ⚠️ Use ALTER LISTING ... PUBLISH (not SET STATE = PUBLISHED)
ALTER LISTING <listing_name> PUBLISH;
```

**Unpublish listing:**
```sql
ALTER LISTING <listing_name> UNPUBLISH;
```

**Delete listing:**
```sql
DROP LISTING <listing_name>;
DROP SHARE <share_name>;  -- Optional
```

**Handle access requests** (if using `REQUEST_AND_APPROVE_IN_SNOWFLAKE`):
```sql
-- View pending requests
SELECT * FROM SNOWFLAKE.DATA_SHARING_USAGE.LISTING_ACCESS_REQUESTS
WHERE LISTING_NAME = '<listing_name>' AND REQUEST_STATUS = 'PENDING';

-- Approve/deny
CALL SYSTEM$APPROVE_LISTING_REQUEST('<request_id>');
CALL SYSTEM$DENY_LISTING_REQUEST('<request_id>', 'Reason');
```

---

## Organization Targets Quick Reference

**⚠️ SYNTAX WARNING**: Use singular `account:` NOT plural `accounts:`

**All accounts discover & access:**
```yaml
organization_targets:
  discovery:
    - all_internal_accounts: true
  access:
    - all_internal_accounts: true
```

**Specific accounts for discovery AND access:**
```yaml
# ⚠️ Use singular "account:" - NOT "accounts:"
organization_targets:
  discovery:
    - account: "ACCOUNT_1"  # ← singular "account:"
    - account: "ACCOUNT_2"
  access:
    - account: "ACCOUNT_1"
    - account: "ACCOUNT_2"
```

**Specific accounts with roles:**
```yaml
organization_targets:
  discovery:
    - all_internal_accounts: true
  access:
    - account: 'finance_account'  # ← singular "account:"
      roles: ['analyst', 'manager']
    - account: 'analytics_account'
```

---

## Stopping Points

- ✋ **Step 1**: After gathering requirements (confirm all inputs before proceeding)
- ✋ **Step 3**: After generating manifest (confirm YAML before execution)

**Resume rule:** Upon user approval, proceed directly to next step without re-asking.

## Output

- Published organizational listing in Internal Marketplace
- Share containing specified database objects
- Snowsight URL for listing management
- ULL (Uniform Listing Locator) for referencing the listing

## Common Errors Quick Reference

| Error | Cause | Fix |
|-------|-------|-----|
| "Share does not currently have a database" | Wrong grant order | Grant DATABASE first, then SCHEMA, then TABLES |
| "invalid identifier 'column_name'" | Wrong column name in usage_examples | Run `DESCRIBE TABLE` first, use actual column names |
| YAML syntax error with `accounts:` | Used plural | Use singular `account:` not `accounts:` |
| "Semantic view not found" | Used `SHOW VIEWS` | Use `SHOW SEMANTIC VIEWS` instead |
| "USAGE not supported for semantic view" | Wrong privilege | Use `GRANT SELECT ON SEMANTIC VIEW` |
| "Missing approver contact" | Field required | Always include `approver_contact` |

## References

For detailed information, **load** these files:

- `references/manifest-reference.md`: All manifest fields, data attributes, data dictionary config, access control setup
- `references/templates.md`: Quick copy-paste templates for common scenarios
- `references/errors.md`: Common errors and troubleshooting guide

## Known Limitations

- Each share can be attached to **one listing only**
- Reader accounts not supported with organizational listings
- Native App listings don't support target roles
- Multiple regions require auto-fulfillment configuration
- Provider studio analytics not supported for org listings

Tags

#data-products

Related Skills

Browse all
v1.0.0

snowpark-connect

cortex-community
|
#snowpark-connect
Cortex
4465.0k
2 hours ago
v1.0.0

semantic-view

cortex-community
[REQUIRED] Use for ALL requests that mention: create, build, debug, fix, troubleshoot, optimize, improve, or analyze a semantic view — AND for requests about VQR suggestions, verified queries, verified query representations, or seeding/generating queries for a semantic view. This is the REQUIRED entry point - even if the request seems simple. DO NOT attempt to create, debug, or generate VQR suggestions for semantic views manually - always invoke this skill first. This skill guides users through creation, setup, auditing, VQR suggestion generation, and SQL generation debugging workflows for semantic views with Cortex Analyst.
#semantic-view
Cortex
4033.7k
2 hours ago
v1.0.0

cortex-code-guide

cortex-community
Complete reference guide for Cortex Code (CoCo) CLI. Use when: learning cortex features, understanding commands, troubleshooting setup, exploring Snowflake tools, managing sessions, configuring agents, keyboard shortcuts, MCP integration. Triggers: how to use cortex, cortex guide, cortex help, cortex commands, getting started, snowflake tools, #table syntax, subagents, sessions, resume, fork, rewind, compact, /agents, configuration.
#cortex-code-guide
Cortex
1953.9k
2 hours ago
v1.0.0

data-quality

cortex-community
trending
Schema-level data quality monitoring, table comparison, dataset popularity analysis, and ad-hoc column quality assessment using Snowflake Data Metric Functions (DMFs) and Access History. Use when user asks about: data quality, schema health, DMF results, quality score, trust my data, quality regression, quality trends, SLA alerting, data metric functions, failing metrics, quality issues, compare tables, data diff, validate migration, table comparison, popular tables, most used tables, unused data, dataset usage, table popularity, listing quality, listing health, listing freshness, provider data quality, consumer data quality, one-time quality check, quick quality scan, check data quality without DMFs, recommend monitors, what should I monitor, DQ coverage gaps, unmonitored tables, DMF coverage report, monitoring health, noisy monitors, silent monitors, misconfigured monitors, DMF cost optimization, investigate DQ incident, why did freshness drop, why did row count drop, correlate violation, multi-dimensional root cause, circuit breaker, pause pipeline on violation, halt bad data propagation, custom DMF, format validation DMF, email format check, value range check, referential integrity DMF, DMF expectations, set threshold, tune DMF threshold, DMF expectation management, attach DMFs, set up DMFs for first time, DMF setup wizard, accepted values, ACCEPTED_VALUES, validate column values, allowed values check, value in set, categorical validation.
#data-quality
Cortex
98313
2 hours ago

Details

Stars166
Installs4.4k
Authorcortex-community
Versionv1.0.0
Updated2 hours ago
LicenseMIT
View Repository
quick install
$ cortex skill install data-products