# What does slack.direct_message.posted prove?

> Prove that Slack holds this message in a direct message conversation. Version 1.0.0, certification Provisional.

Canonical: https://provely.sh/verify/slack/slack.direct_message.posted  
Last reviewed: 2026-09-05  
Skill version: 0.1.0  
Certification: provisional  

**Prove that Slack holds this message in a direct message conversation. The post event states the channel_type im, so the message reached a direct message conversation and not a public channel. It does not prove which person.**

*Contract identity.*

| Fact | Value |
| --- | --- |
| Contract id | `slack.direct_message.posted` |
| Version | `1.0.0` |
| Hash | `8619feda2b62da0a` |
| Completion level | `posted_to_user` |
| Publisher | Built and signed by Provely. |
| Certification | Provisional |
| Skill | [Slack Web API 0.1.0](/verify/slack) |
| Valid for provider API versions | `fp_2026_09` |

## What is the intent?

Send the message to the person as a direct message.

## What is the subject and the action?

| Member | Value |
| --- | --- |
| Subject type | `slack.message` |
| Subject identity | message_ts = `$action.result.ts` |
| Canonical effect | `messaging.message_post` |
| Provider operation | `POST /chat.postMessage` |
| Idempotency | none, retry is not safe |

## How does the evidence correlate with this operation?

| Strategy | Assurance | Keys | Required |
| --- | --- | --- | --- |
| `resource_id` | strong | message_ts from `$action.result.ts` | yes |
| `fingerprint` | weak | channel from `$input.channel_id`; text from `$input.text` | no |

## Which evidence does the contract require?

Minimum evidence level `E2`. An independent channel is required. Minimum channels: 1.

| Channel | Level | Independence | Verifier | Description |
| --- | --- | --- | --- | --- |
| `message_readback` | E2 | provider readback | `http` | Read the one message of the conversation that carries this ts. The documented recipe sets oldest to the ts, inclusive to true, and limit to 1. A condition path is $observed.message_readback.messages[0].ts. The path $observed.message_readback.ok states whether Slack answered. |
| `direct_message_posted_event` | E3 | provider event | `webhook` | The message.im events of this message in the intended direct message conversation. The webhook verifier returns {events, count, latest, earliest, types, duplicates_dropped}. The value im of channel_type separates a direct message from every other conversation. |
| `message_conversation_posts` | E3 | provider event | `webhook` | The messages of this text in this conversation since the operation started. The webhook verifier returns {events, count, latest, earliest, types, duplicates_dropped}. A count above one shows a second post, and chat.postMessage has no idempotency key to stop it. |

## Which conditions must all hold for VERIFIED?

| Condition | Meaning | Path | Operator | Expected | Evidence |
| --- | --- | --- | --- | --- | --- |
| `read_succeeded` | Slack answered the read. Every Web API response carries the boolean member ok, and Slack answers HTTP 200 for a failure as well, so the status code states nothing. | `$observed.message_readback.ok` | `eq` | `true` | `message_readback` |
| `message_present` | The conversation holds exactly one message at this ts. A hidden message, such as a deleted one, leaves the list. | `$observed.message_readback.messages` | `array_length` | `1` | `message_readback` |
| `message_ts_matches` | The ts of the message equals the ts of the answer of chat.postMessage. The member is a string in both places. | `$observed.message_readback.messages[0].ts` | `eq` | `$action.result.ts` | `message_readback` |
| `message_text_matches` | The text of the message equals the text that the intent stated. | `$observed.message_readback.messages[0].text` | `eq` | `$input.text` | `message_readback` |
| `message_posted_by_an_app` | The message carries the subtype bot_message, so an integration posted it. A message of a person states no subtype. | `$observed.message_readback.messages[0].subtype` | `eq` | `bot_message` | `message_readback` |
| `message_posted_in_the_window` | Slack posted the message after the operation started. The ts of a message is a Unix timestamp, and the bounds oldest and latest read the same values. | `$observed.message_readback.messages[0].ts` | `time_after` | `$operation.created_at` | `message_readback` |
| `direct_message_event_names_an_im_conversation` | At least one message.im event of this message names the conversation of the intent and states the channel_type im. A workspace with no event subscription cannot verify this level. | `$observed.direct_message_posted_event.count` | `gte` | `1` | `direct_message_posted_event` |

## Which conditions give CONTRADICTED?

| Condition | Class | Reason | Path | Operator | Expected |
| --- | --- | --- | --- | --- | --- |
| `message_text_differs` | wrong subject | Slack holds a different text than the intent stated. Ask a person. | `$observed.message_readback.messages[0].text` | `ne` | `$input.text` |
| `message_predates_operation` | pre existing state | The message is older than the operation. It proves nothing. | `$observed.message_readback.messages[0].ts` | `time_before` | `$operation.created_at` |
| `duplicate_message_present` | duplicate side effect | Slack holds more than one message of this text in this conversation since the operation started. Do not retry. | `$observed.message_conversation_posts.count` | `gt` | `1` |

## Which observed states map to a verdict before completion?

| Rule | Match | Verdict | Reason |
| --- | --- | --- | --- |
| `read_refused` | `$observed.message_readback.ok` eq `false` | UNVERIFIABLE | Slack answered the read with the status code 200 and the member ok false. The answer is a refusal and it is no evidence. |

## How long does the runtime observe?

| Timing member | Value |
| --- | --- |
| Initial delay | 1000 ms |
| Poll interval | 5000 ms |
| Backoff | exponential factor 2, max 60000 ms |
| Maximum attempts | 40 |
| Timeout | 900000 ms |
| Stale read window | 20000 ms |
| On timeout | UNVERIFIABLE (`evidence_unavailable_before_timeout`), escalated to a person |

## Where do these rules come from?

- `slack.openapi#/paths/~1chat.postMessage/post`: [slack.openapi](https://docs.slack.dev/reference/methods), retrieved 2026-09-08
- `slack.docs.post_message#post-a-message/post-to-a-direct-message-conversation`: [slack.docs.post_message](https://docs.slack.dev/reference/methods/chat.postMessage), retrieved 2026-09-08
- `slack.docs.conversations#conversations/open-a-direct-message-conversation`: [slack.docs.conversations](https://docs.slack.dev/reference/methods/conversations.history), retrieved 2026-09-08
- `slack.docs.conversations#conversations/read-one-conversation`: [slack.docs.conversations](https://docs.slack.dev/reference/methods/conversations.history), retrieved 2026-09-08
- `slack.openapi#/components/schemas/ConversationsHistoryResponse/properties/ok`: [slack.openapi](https://docs.slack.dev/reference/methods), retrieved 2026-09-08
- `slack.docs.web_api#web-api/the-ok-member`: [slack.docs.web_api](https://docs.slack.dev/apis/web-api/), retrieved 2026-09-08
- `slack.docs.web_api#web-api/a-failure-carries-the-status-code-200`: [slack.docs.web_api](https://docs.slack.dev/apis/web-api/), retrieved 2026-09-08
- `slack.openapi#/components/schemas/ConversationsHistoryResponse/properties/messages`: [slack.openapi](https://docs.slack.dev/reference/methods), retrieved 2026-09-08
- `slack.docs.conversations#conversations/read-one-message`: [slack.docs.conversations](https://docs.slack.dev/reference/methods/conversations.history), retrieved 2026-09-08
- `slack.docs.conversations#conversations/a-hidden-message`: [slack.docs.conversations](https://docs.slack.dev/reference/methods/conversations.history), retrieved 2026-09-08
- `slack.openapi#/components/schemas/Message/properties/ts`: [slack.openapi](https://docs.slack.dev/reference/methods), retrieved 2026-09-08
- `slack.openapi#/components/schemas/ChatPostMessageResponse/properties/ts`: [slack.openapi](https://docs.slack.dev/reference/methods), retrieved 2026-09-08
- `slack.openapi#/components/schemas/Message/properties/text`: [slack.openapi](https://docs.slack.dev/reference/methods), retrieved 2026-09-08
- `slack.docs.post_message#post-a-message/the-response`: [slack.docs.post_message](https://docs.slack.dev/reference/methods/chat.postMessage), retrieved 2026-09-08
- `slack.openapi#/components/schemas/Message/properties/subtype`: [slack.openapi](https://docs.slack.dev/reference/methods), retrieved 2026-09-08
- `slack.docs.post_message#post-a-message/the-subtype-of-a-message-that-an-app-posts`: [slack.docs.post_message](https://docs.slack.dev/reference/methods/chat.postMessage), retrieved 2026-09-08
- `slack.docs.conversations#conversations/read-the-history-of-a-conversation`: [slack.docs.conversations](https://docs.slack.dev/reference/methods/conversations.history), retrieved 2026-09-08
- `slack.events#/events/message.im`: [slack.events](https://docs.slack.dev/reference/events/message), retrieved 2026-09-08
- `slack.docs.events#events-api/the-message-subscriptions`: [slack.docs.events](https://docs.slack.dev/apis/events-api/), retrieved 2026-09-08
- `slack.openapi#/components/schemas/Conversation/properties/is_im`: [slack.openapi](https://docs.slack.dev/reference/methods), retrieved 2026-09-08

### Can slack.direct_message.posted return VERIFIED from the action response alone?

No. The minimum evidence level is E2. The action response is E1. The completion conditions read `message_readback` and `direct_message_posted_event`.

### What happens after the timeout?

The verdict is UNVERIFIABLE with the reason `evidence_unavailable_before_timeout`. The operation goes to a person for review.

## Read next

- [Read the Slack Web API verification page](https://provely.sh/verify/slack)
- [Read the slack.message.posted contract](https://provely.sh/verify/slack/slack.message.posted)
- [Read what a completion contract contains](https://provely.sh/contracts)
- [Read what a receipt proves](https://provely.sh/docs/receipts)
