Skip to content

Conversation

@srtaalej
Copy link
Contributor

@srtaalej srtaalej commented Oct 30, 2025

Summary

This PR adds the following slackLists methods to the slack_sdk: access.delete, access.set, create, download.get, download.start, items.create, items.delete, items.deleteMultiple, items.info, items.list, items.update, update

Testing

▶️ expand test example
 const list = await app.client.slackLists.create({
      name: 'Test List - SlackLists API',
      description_blocks: [
        {
          type: 'rich_text',
          elements: [
            {
              type: 'rich_text_section',
              elements: [
                {
                  type: 'text',
                  text: 'List to keep track of tasks!',
                },
              ],
            },
          ],
        },
      ],
      schema: [
        {
          key: 'task_name',
          name: 'Task Name',
          type: 'text',
          is_primary_column: true,
        },
        {
          key: 'due_date',
          name: 'Due Date',
          type: 'date',
        },
        {
          key: 'status',
          name: 'Status',
          type: 'select',
          options: {
            choices: [
              { value: 'not_started', label: 'Not Started', color: 'red' },
              { value: 'in_progress', label: 'In Progress', color: 'yellow' },
              { value: 'completed', label: 'Completed', color: 'green' },
            ],
          },
        },
        {
          key: 'assignee',
          name: 'Assignee',
          type: 'user',
        },
      ],
    });
    console.log('List created:', list);
    const listId = list.list_id;

    // extract column IDs from the response (map key -> id)
    const keyToId = {};
    if (list.list_metadata?.schema) {
      for (const col of list.list_metadata.schema) {
        keyToId[col.key] = col.id;
      }
    }
    const taskNameColId = keyToId['task_name'];
    console.log('Column IDs:', keyToId);

    const response = await app.client.slackLists.access.set({
      list_id: listId,
      access_level: 'write',
      user_ids: ['U09G4FG3TRN'],
    });
    console.log('Access set:', response);

    const createItemResponse = await app.client.slackLists.items.create({
      list_id: listId,
      initial_fields: [
        {
          column_id: taskNameColId,
          rich_text: [
            {
              type: 'rich_text',
              elements: [
                {
                  type: 'rich_text_section',
                  elements: [
                    {
                      type: 'text',
                      text: 'CLI app unlink command',
                    },
                  ],
                },
              ],
            },
          ],
        },
      ],
    });
    console.log('Item created:', createItemResponse);
    const itemId = createItemResponse.id;

    if (itemId) {
      await app.client.slackLists.items.info({
        list_id: listId,
        id: itemId,
        include_is_subscribed: true,
      });
      console.log('Item info retrieved');

      await app.client.slackLists.items.update({
        list_id: listId,
        cells: [
          {
            row_id: itemId,
            column_id: taskNameColId,
            checkbox: true,
          },
        ],
      });
      console.log('Item updated');
    }

    const listItemsResponse = await app.client.slackLists.items.list({
      list_id: listId,
      limit: 50,
    });
    console.log('Items listed:', listItemsResponse);

    const downloadStartResponse = await app.client.slackLists.download.start({
      list_id: listId,
      include_archived: false,
    });
    console.log('Download started:', downloadStartResponse);
    const jobId = downloadStartResponse.job_id;

    if (jobId) {
      await app.client.slackLists.download.get({
        list_id: listId,
        job_id: jobId,
      });
      console.log('Download status retrieved');
    }

    if (itemId) {
      await app.client.slackLists.items.delete({
        list_id: listId,
        id: itemId,
      });
      console.log('Item deleted');
    }

    await app.client.slackLists.items.deleteMultiple({
      list_id: listId,
      ids: ['item1', 'item2'],
    });
    console.log('Multiple items deleted');

    await app.client.slackLists.access.delete({
      list_id: listId,
      user_ids: ['U09G4FG3TRN'],
    });
    console.log('Access removed');

Requirements (place an x in each [ ])

@srtaalej srtaalej self-assigned this Oct 30, 2025
@codecov
Copy link

codecov bot commented Oct 30, 2025

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 93.09%. Comparing base (b396b04) to head (921f023).
⚠️ Report is 2 commits behind head on main.
✅ All tests successful. No failed tests found.

Additional details and impacted files
@@            Coverage Diff             @@
##             main    #2421      +/-   ##
==========================================
+ Coverage   93.02%   93.09%   +0.06%     
==========================================
  Files          40       40              
  Lines       11127    11238     +111     
  Branches      713      713              
==========================================
+ Hits        10351    10462     +111     
  Misses        764      764              
  Partials       12       12              
Flag Coverage Δ
cli-hooks 95.23% <ø> (ø)
cli-test 94.79% <ø> (ø)
oauth 77.39% <ø> (ø)
socket-mode 61.87% <ø> (ø)
web-api 98.11% <100.00%> (+0.03%) ⬆️
webhook 96.66% <ø> (ø)

Flags with carried forward coverage won't be shown. Click here to find out more.

🚀 New features to boost your workflow:
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@srtaalej srtaalej requested review from mwbrooks and zimeg and removed request for zimeg October 30, 2025 21:48
@srtaalej srtaalej added semver:minor enhancement M-T: A feature request for new functionality pkg:web-api applies to `@slack/web-api` labels Oct 30, 2025
@zimeg zimeg linked an issue Oct 31, 2025 that may be closed by this pull request
7 tasks
Copy link
Member

@zimeg zimeg left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@srtaalej Exciting times for these methods I think! 📝

I left a few findings to match adjacent implementations and more thoughts on typing here. We might want to discuss how schemas are handled since I believe developers will want to import these for use elsewhere.

So open to your thoughts on this, and I look forward to continued testing 🧪 ✨

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔏 note: Let's revisit these responses after a java implementation has been released!

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🚧 issue: Not having all response types can cause issues with typescript checks. We'll want to revisit these outputs with automated response generation soon since I notice some missing values in these types:

  • list_id: String
  • list_metadata: Object
{"ok":true,"list_id":"F09ULKZ15TJ","list_metadata":{"schema":[{"key":"task_name","name":"Task Name","is_primary_column":true,"type":"text","id":"Col09U582EPS5"},{"key":"due_date","name":"Due Date","is_primary_column":false,"type":"date","id":"Col09TKUQDVJ7"},{"key":"status","name":"Status","is_primary_column":false,"type":"select","options":{"choices":[{"value":"not_started","label":"Not Started","color":"red"},{"value":"in_progress","label":"In Progress","color":"yellow"},{"value":"completed","label":"Completed","color":"green"}],"show_member_name":true},"id":"Col09TNT4AX0V"},{"key":"assignee","name":"Assignee","is_primary_column":false,"type":"user","id":"Col09TAT5Q3ST"}],"subtask_schema":[{"key":"task_name","name":"Task Name","is_primary_column":true,"type":"text","id":"Col09U582EPS5"}]}}

@srtaalej srtaalej force-pushed the ale-feat-webapi-slacklists branch from ca0e64a to a2657f5 Compare November 13, 2025 17:31
@srtaalej srtaalej requested a review from zimeg November 13, 2025 17:35
@zimeg zimeg changed the title feat(web-api): add slacklists.create method feat(web-api): add slackLists methods Nov 19, 2025
Copy link
Member

@zimeg zimeg left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@srtaalej Sweeeeet! This is a nice changeset in the works ⚙️ ✨

I'm requesting a few changes before this merges, with a focus on some of the following:

  • JSdoc for method arguments
  • Response shapes from the API
  • Examples in testing

Overall things are solid but the last point makes me wonder more if introducing a schema now is meaningful... Let me know if I can add more detail or help otherwise!

@srtaalej srtaalej requested a review from a team as a code owner November 19, 2025 20:23
@srtaalej srtaalej requested a review from zimeg November 19, 2025 23:06
@zimeg
Copy link
Member

zimeg commented Nov 20, 2025

🧪 Testing steps - similar to slackapi/python-slack-sdk#1772!

Details
const listCreateResponse = await app.client.slackLists.create({
  name: "Test List - SlackLists API",
  description_blocks: [
    {
      type: "rich_text",
      elements: [
        {
          type: "rich_text_section",
          elements: [
            { type: "text", text: "List to keep track of tasks!" }
          ]
        }
      ]
    }
  ],
  schema: [
    {
      key: "task_name",
      name: "Task Name",
      type: "text",
      is_primary_column: true,
    },
    {
      key: "due_date",
      name: "Due Date",
      type: "date",
    },
    {
      key: "status",
      name: "Status",
      type: "select",
      options: {
        choices: [
          { value: "not_started", label: "Not Started", color: "red" },
          { value: "in_progress", label: "In Progress", color: "yellow" },
          { value: "completed", label: "Completed", color: "green" },
        ],
      },
    },
    {
      key: "assignee",
      name: "Assignee",
      type: "user",
    },
  ],
});

// extract fields
const keyToId = Object.fromEntries(
  listCreateResponse.list_metadata.schema.map(col => [col.key, col.id])
);

const taskNameColId = keyToId["task_name"];
const listId = listCreateResponse.list_id;

// -----------------------
// Set access permissions
// -----------------------
await app.client.slackLists.access.set({
  list_id: listId,
  access_level: "write",
  user_ids: ["U04051AF9NJ"],
});

// -----------------------
// Create list items
// -----------------------
const itemResponse1 = await app.client.slackLists.items.create({
  list_id: listId,
  initial_fields: [
    {
      column_id: taskNameColId,
      rich_text: [
        {
          type: "rich_text",
          elements: [
            {
              type: "rich_text_section",
              elements: [{ type: "text", text: "CLI app unlink command" }],
            },
          ],
        },
      ],
    },
  ],
});

// four empty items
const itemResponse2 = await app.client.slackLists.items.create({ list_id: listId });
const itemResponse3 = await app.client.slackLists.items.create({ list_id: listId });
const itemResponse4 = await app.client.slackLists.items.create({ list_id: listId });
const itemResponse5 = await app.client.slackLists.items.create({ list_id: listId });

const itemId1 = itemResponse1.item.id;
const itemId2 = itemResponse2.item.id;
const itemId3 = itemResponse3.item.id;
const itemId4 = itemResponse4.item.id;
const itemId5 = itemResponse5.item.id;

// -----------------------
// Delete items
// -----------------------
await app.client.slackLists.items.delete({
  list_id: listId,
  id: itemId3,
});

await app.client.slackLists.items.deleteMultiple({
  list_id: listId,
  ids: [itemId4, itemId5],
});

// -----------------------
// Retrieve a single item
// -----------------------
const itemInfo = await app.client.slackLists.items.info({
  list_id: listId,
  id: itemId1,
  include_is_subscribed: true,
});

// -----------------------
// Retrieve all items
// -----------------------
const itemsList = await app.client.slackLists.items.list({
  list_id: listId,
  limit: 50,
  archived: false,
});

// -----------------------
// Download full list data
// -----------------------
const downloadStart = await app.client.slackLists.download.start({
  list_id: listId,
  include_archived: false,
});

const downloadGet = await app.client.slackLists.download.get({
  list_id: listId,
  job_id: downloadStart.job_id,
});

// -----------------------
// Update a list item
// -----------------------
await app.client.slackLists.items.update({
  list_id: listId,
  cells: [
    {
      column_id: taskNameColId,
      row_id: itemId1,
      rich_text: [
        {
          type: "rich_text",
          elements: [
            {
              type: "rich_text_section",
              elements: [{ type: "text", text: "Updated text" }],
            },
          ],
        },
      ],
    },
  ],
});

// -----------------------
// Update list metadata
// -----------------------
await app.client.slackLists.update({
  id: listId,
  name: "Test List - UPDATED",
  description_blocks: [
    {
      type: "rich_text",
      elements: [
        {
          type: "rich_text_section",
          elements: [
            { type: "text", text: "This list has been updated via API" },
          ],
        },
      ],
    },
  ],
  todo_mode: false,
});

// -----------------------
// Remove access
// -----------------------
await app.client.slackLists.access.delete({
  list_id: listId,
  user_ids: ["U04051AF9NJ"],
});

console.log("Done!");

Copy link
Member

@zimeg zimeg left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@srtaalej Thanks for making these changes all the more! 📜 🎉

I noticed a few small findings around testing IDs still and I hold caution to incomplete response types, but I don't believe that's blocking.

Let me know what you think about the response types though! I notice solid progress with slackapi/java-slack-sdk#1537 that we can perhaps wait for?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🚧 issue: Not having all response types can cause issues with typescript checks. We'll want to revisit these outputs with automated response generation soon since I notice some missing values in these types:

  • list_id: String
  • list_metadata: Object
{"ok":true,"list_id":"F09ULKZ15TJ","list_metadata":{"schema":[{"key":"task_name","name":"Task Name","is_primary_column":true,"type":"text","id":"Col09U582EPS5"},{"key":"due_date","name":"Due Date","is_primary_column":false,"type":"date","id":"Col09TKUQDVJ7"},{"key":"status","name":"Status","is_primary_column":false,"type":"select","options":{"choices":[{"value":"not_started","label":"Not Started","color":"red"},{"value":"in_progress","label":"In Progress","color":"yellow"},{"value":"completed","label":"Completed","color":"green"}],"show_member_name":true},"id":"Col09TNT4AX0V"},{"key":"assignee","name":"Assignee","is_primary_column":false,"type":"user","id":"Col09TAT5Q3ST"}],"subtask_schema":[{"key":"task_name","name":"Task Name","is_primary_column":true,"type":"text","id":"Col09U582EPS5"}]}}

@leminhhieu98py
Copy link

leminhhieu98py commented Nov 21, 2025

Hello. Currently, I can add a new list by using:

const web = new WebClient(process.env.SLACK_BOT_TOKEN);
const list = await web.apiCall("slackLists.create", {
      name: "todo list",
});

My issue:

  • I do not see the list todo list in the Slack UI

Please guide me. Thanks in advanced ~

@mwbrooks
Copy link
Member

mwbrooks commented Nov 21, 2025

My issue:

  • I do not see the list todo list in the Slack UI

@leminhhieu98py This is just an educated guess because I haven't tried the new API myself: since the new list is created with your SLACK_BOT_TOKEN it will be owned by your Slack bot. I believe by default, all lists are only shared to the owner, which is why you can't see it. You'll need to update who can access the list in order for a user other than your bot to view/edit it. After creating the list, you can give access to other users or channels with the slacklists.access.set method. Alternatively, you could create the list with a User Token obtained through Slack OAuth to create the list on behalf of a specific user.

@mwbrooks mwbrooks added this to the [email protected] milestone Nov 25, 2025
Copy link
Member

@mwbrooks mwbrooks left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ Looks good @srtaalej! This was a monster method collection to add, so thanks for rolling up your sleeves and diving into it! 🙇🏻

✏️ I've left a couple suggestions and nits. Please use your discretion on whether on what to accept or not. Functionally, things look great!

👾 Absolutely love the example that you included in your PR! Let's include that in the future Release Notes. I especially like how it's a complete working example that flows through the usage. I edited your PR description to include javascript syntax highlighting, btw.

🚀 Looking forward to seeing this published!

@srtaalej srtaalej merged commit be50825 into main Nov 25, 2025
57 checks passed
@srtaalej srtaalej deleted the ale-feat-webapi-slacklists branch November 25, 2025 19:33
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement M-T: A feature request for new functionality pkg:web-api applies to `@slack/web-api` semver:minor

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Missing all slackLists endpoints.

5 participants