0
# Webhooks
1
2
Discord webhook clients for sending messages without a bot presence. Discord.py provides both synchronous and asynchronous webhook implementations with support for embeds, files, thread management, and message editing.
3
4
## Capabilities
5
6
### Async Webhook Client
7
8
Asynchronous webhook client for sending messages with full Discord features.
9
10
```python { .api }
11
class Webhook:
12
"""
13
Asynchronous Discord webhook client.
14
"""
15
def __init__(self, url: str, *, session: Optional[aiohttp.ClientSession] = None): ...
16
17
# Webhook properties
18
id: int # Webhook ID
19
type: WebhookType # Webhook type
20
guild_id: Optional[int] # Guild ID if guild webhook
21
channel_id: Optional[int] # Channel ID if channel webhook
22
user: Optional[User] # User who created webhook
23
name: Optional[str] # Webhook name
24
avatar: Optional[str] # Webhook avatar hash
25
token: Optional[str] # Webhook token
26
application_id: Optional[int] # Application ID for application webhooks
27
source_guild: Optional[PartialWebhookGuild] # Source guild for follower webhooks
28
source_channel: Optional[PartialWebhookChannel] # Source channel for follower webhooks
29
url: str # Webhook URL
30
31
# Class methods for creation
32
@classmethod
33
def from_url(cls, url: str, *, session: Optional[aiohttp.ClientSession] = None) -> Webhook:
34
"""Create webhook from URL."""
35
36
@classmethod
37
async def from_state(
38
cls,
39
data: Dict[str, Any],
40
*,
41
session: Optional[aiohttp.ClientSession] = None
42
) -> Webhook:
43
"""Create webhook from Discord API data."""
44
45
# Message sending
46
async def send(
47
self,
48
content: Optional[str] = None,
49
*,
50
username: Optional[str] = None,
51
avatar_url: Optional[str] = None,
52
tts: bool = False,
53
ephemeral: bool = False,
54
file: Optional[File] = None,
55
files: Optional[List[File]] = None,
56
embed: Optional[Embed] = None,
57
embeds: Optional[List[Embed]] = None,
58
allowed_mentions: Optional[AllowedMentions] = None,
59
thread: Optional[Snowflake] = None,
60
thread_name: Optional[str] = None,
61
wait: bool = False,
62
suppress_embeds: bool = False
63
) -> Optional[WebhookMessage]:
64
"""
65
Send a message via webhook.
66
67
Parameters:
68
- content: Message content
69
- username: Override webhook username
70
- avatar_url: Override webhook avatar
71
- tts: Whether message is text-to-speech
72
- ephemeral: Whether message is ephemeral (interaction webhooks only)
73
- file: Single file to upload
74
- files: Multiple files to upload
75
- embed: Single embed to send
76
- embeds: Multiple embeds to send
77
- allowed_mentions: Mention settings
78
- thread: Thread to send message in
79
- thread_name: Create new thread with this name
80
- wait: Whether to wait for message confirmation
81
- suppress_embeds: Whether to suppress link embeds
82
83
Returns:
84
WebhookMessage if wait=True, None otherwise
85
"""
86
87
# Message management
88
async def fetch_message(self, id: int, *, thread: Optional[Snowflake] = None) -> WebhookMessage:
89
"""
90
Fetch a webhook message by ID.
91
92
Parameters:
93
- id: Message ID to fetch
94
- thread: Thread containing the message
95
96
Returns:
97
WebhookMessage object
98
"""
99
100
async def edit_message(
101
self,
102
message_id: int,
103
*,
104
content: Optional[str] = None,
105
embed: Optional[Embed] = None,
106
embeds: Optional[List[Embed]] = None,
107
file: Optional[File] = None,
108
files: Optional[List[File]] = None,
109
attachments: Optional[List[Attachment]] = None,
110
allowed_mentions: Optional[AllowedMentions] = None,
111
thread: Optional[Snowflake] = None
112
) -> WebhookMessage:
113
"""
114
Edit a webhook message.
115
116
Parameters:
117
- message_id: ID of message to edit
118
- content: New message content
119
- embed: New embed
120
- embeds: New embeds
121
- file: New file to upload
122
- files: New files to upload
123
- attachments: Attachments to keep
124
- allowed_mentions: Mention settings
125
- thread: Thread containing the message
126
127
Returns:
128
Edited WebhookMessage
129
"""
130
131
async def delete_message(self, message_id: int, *, thread: Optional[Snowflake] = None) -> None:
132
"""
133
Delete a webhook message.
134
135
Parameters:
136
- message_id: ID of message to delete
137
- thread: Thread containing the message
138
"""
139
140
# Webhook management
141
async def edit(
142
self,
143
*,
144
name: Optional[str] = None,
145
avatar: Optional[bytes] = None,
146
channel: Optional[TextChannel] = None,
147
reason: Optional[str] = None
148
) -> Webhook:
149
"""
150
Edit webhook properties.
151
152
Parameters:
153
- name: New webhook name
154
- avatar: New webhook avatar
155
- channel: New webhook channel
156
- reason: Reason for audit log
157
158
Returns:
159
Updated Webhook object
160
"""
161
162
async def delete(self, *, reason: Optional[str] = None) -> None:
163
"""
164
Delete the webhook.
165
166
Parameters:
167
- reason: Reason for audit log
168
"""
169
170
# Thread management
171
async def create_thread(
172
self,
173
*,
174
name: str,
175
message: Optional[WebhookMessage] = None,
176
auto_archive_duration: int = 1440,
177
rate_limit_per_user: Optional[int] = None,
178
reason: Optional[str] = None
179
) -> Thread:
180
"""
181
Create a thread via webhook.
182
183
Parameters:
184
- name: Thread name
185
- message: Message to start thread from
186
- auto_archive_duration: Auto-archive duration in minutes
187
- rate_limit_per_user: Slowmode delay in seconds
188
- reason: Reason for audit log
189
190
Returns:
191
Created Thread object
192
"""
193
194
class WebhookMessage:
195
"""
196
Represents a message sent by a webhook.
197
"""
198
def __init__(self, *, data: Dict[str, Any], state, webhook: Webhook): ...
199
200
# Message properties (similar to regular Message)
201
id: int # Message ID
202
webhook_id: int # Webhook ID
203
channel: PartialMessageable # Channel message was sent in
204
guild: Optional[Guild] # Guild if sent in guild channel
205
content: str # Message content
206
clean_content: str # Content with mentions resolved
207
created_at: datetime # Message creation timestamp
208
edited_at: Optional[datetime] # Last edit timestamp
209
tts: bool # Whether message is text-to-speech
210
mention_everyone: bool # Whether message mentions @everyone
211
mentions: List[User] # Mentioned users
212
channel_mentions: List[GuildChannel] # Mentioned channels
213
role_mentions: List[Role] # Mentioned roles
214
attachments: List[Attachment] # File attachments
215
embeds: List[Embed] # Rich embeds
216
reactions: List[Reaction] # Message reactions
217
pinned: bool # Whether message is pinned
218
type: MessageType # Message type
219
flags: MessageFlags # Message flags
220
thread: Optional[Thread] # Thread if message is in thread
221
components: List[Component] # UI components
222
223
# Message management
224
async def edit(
225
self,
226
*,
227
content: Optional[str] = None,
228
embed: Optional[Embed] = None,
229
embeds: Optional[List[Embed]] = None,
230
file: Optional[File] = None,
231
files: Optional[List[File]] = None,
232
attachments: Optional[List[Attachment]] = None,
233
allowed_mentions: Optional[AllowedMentions] = None
234
) -> WebhookMessage:
235
"""Edit the webhook message."""
236
237
async def delete(self, *, delay: Optional[float] = None) -> None:
238
"""Delete the webhook message."""
239
240
# Message interactions
241
async def add_reaction(self, emoji: Union[Emoji, Reaction, PartialEmoji, str]) -> None:
242
"""Add a reaction to the message."""
243
244
async def remove_reaction(self, emoji: Union[Emoji, Reaction, PartialEmoji, str], member: Member) -> None:
245
"""Remove a reaction from the message."""
246
247
async def clear_reactions(self) -> None:
248
"""Clear all reactions from the message."""
249
250
async def pin(self, *, reason: Optional[str] = None) -> None:
251
"""Pin the message."""
252
253
async def unpin(self, *, reason: Optional[str] = None) -> None:
254
"""Unpin the message."""
255
256
def to_reference(self) -> MessageReference:
257
"""Create a reference to this message for replies."""
258
259
class PartialWebhookGuild:
260
"""
261
Partial guild information from webhook data.
262
"""
263
def __init__(self, data: Dict[str, Any]): ...
264
265
id: int # Guild ID
266
name: str # Guild name
267
icon: Optional[str] # Guild icon hash
268
269
class PartialWebhookChannel:
270
"""
271
Partial channel information from webhook data.
272
"""
273
def __init__(self, data: Dict[str, Any]): ...
274
275
id: int # Channel ID
276
name: str # Channel name
277
type: ChannelType # Channel type
278
```
279
280
### Sync Webhook Client
281
282
Synchronous webhook client for use without async/await.
283
284
```python { .api }
285
class SyncWebhook:
286
"""
287
Synchronous Discord webhook client.
288
"""
289
def __init__(self, url: str, *, session: Optional[requests.Session] = None): ...
290
291
# Same properties as async Webhook
292
id: int
293
type: WebhookType
294
guild_id: Optional[int]
295
channel_id: Optional[int]
296
user: Optional[User]
297
name: Optional[str]
298
avatar: Optional[str]
299
token: Optional[str]
300
application_id: Optional[int]
301
source_guild: Optional[PartialWebhookGuild]
302
source_channel: Optional[PartialWebhookChannel]
303
url: str
304
305
# Class methods
306
@classmethod
307
def from_url(cls, url: str, *, session: Optional[requests.Session] = None) -> SyncWebhook:
308
"""Create sync webhook from URL."""
309
310
# Message sending (synchronous versions)
311
def send(
312
self,
313
content: Optional[str] = None,
314
*,
315
username: Optional[str] = None,
316
avatar_url: Optional[str] = None,
317
tts: bool = False,
318
file: Optional[File] = None,
319
files: Optional[List[File]] = None,
320
embed: Optional[Embed] = None,
321
embeds: Optional[List[Embed]] = None,
322
allowed_mentions: Optional[AllowedMentions] = None,
323
thread: Optional[Snowflake] = None,
324
wait: bool = False
325
) -> Optional[SyncWebhookMessage]:
326
"""Send a message via webhook (synchronous)."""
327
328
def fetch_message(self, id: int, *, thread: Optional[Snowflake] = None) -> SyncWebhookMessage:
329
"""Fetch a webhook message by ID (synchronous)."""
330
331
def edit_message(
332
self,
333
message_id: int,
334
*,
335
content: Optional[str] = None,
336
embed: Optional[Embed] = None,
337
embeds: Optional[List[Embed]] = None,
338
file: Optional[File] = None,
339
files: Optional[List[File]] = None,
340
attachments: Optional[List[Attachment]] = None,
341
allowed_mentions: Optional[AllowedMentions] = None,
342
thread: Optional[Snowflake] = None
343
) -> SyncWebhookMessage:
344
"""Edit a webhook message (synchronous)."""
345
346
def delete_message(self, message_id: int, *, thread: Optional[Snowflake] = None) -> None:
347
"""Delete a webhook message (synchronous)."""
348
349
def edit(
350
self,
351
*,
352
name: Optional[str] = None,
353
avatar: Optional[bytes] = None,
354
reason: Optional[str] = None
355
) -> SyncWebhook:
356
"""Edit webhook properties (synchronous)."""
357
358
def delete(self, *, reason: Optional[str] = None) -> None:
359
"""Delete the webhook (synchronous)."""
360
361
class SyncWebhookMessage:
362
"""
363
Synchronous version of WebhookMessage.
364
Same properties and methods as WebhookMessage but synchronous.
365
"""
366
def __init__(self, *, data: Dict[str, Any], state, webhook: SyncWebhook): ...
367
368
# Same properties as WebhookMessage
369
id: int
370
webhook_id: int
371
channel: PartialMessageable
372
guild: Optional[Guild]
373
content: str
374
clean_content: str
375
created_at: datetime
376
edited_at: Optional[datetime]
377
attachments: List[Attachment]
378
embeds: List[Embed]
379
# ... (all other properties)
380
381
# Synchronous methods
382
def edit(self, **kwargs) -> SyncWebhookMessage:
383
"""Edit the webhook message (synchronous)."""
384
385
def delete(self, *, delay: Optional[float] = None) -> None:
386
"""Delete the webhook message (synchronous)."""
387
388
def add_reaction(self, emoji: Union[Emoji, Reaction, PartialEmoji, str]) -> None:
389
"""Add a reaction to the message (synchronous)."""
390
391
def remove_reaction(self, emoji: Union[Emoji, Reaction, PartialEmoji, str], member: Member) -> None:
392
"""Remove a reaction from the message (synchronous)."""
393
394
def clear_reactions(self) -> None:
395
"""Clear all reactions from the message (synchronous)."""
396
397
def pin(self, *, reason: Optional[str] = None) -> None:
398
"""Pin the message (synchronous)."""
399
400
def unpin(self, *, reason: Optional[str] = None) -> None:
401
"""Unpin the message (synchronous)."""
402
```
403
404
### Webhook Types & Enums
405
406
```python { .api }
407
class WebhookType(Enum):
408
"""Webhook type enumeration."""
409
incoming = 1 # Incoming webhook
410
channel_follower = 2 # Channel follower webhook
411
application = 3 # Application webhook (for slash commands)
412
```
413
414
## Usage Examples
415
416
### Basic Webhook Usage
417
418
```python
419
import discord
420
import asyncio
421
import aiohttp
422
423
async def send_webhook_message():
424
"""Send a message via webhook."""
425
webhook_url = "https://discord.com/api/webhooks/YOUR_WEBHOOK_URL"
426
427
async with aiohttp.ClientSession() as session:
428
webhook = discord.Webhook.from_url(webhook_url, session=session)
429
430
# Send simple message
431
await webhook.send("Hello from webhook!")
432
433
# Send message with custom username and avatar
434
await webhook.send(
435
"Custom webhook message!",
436
username="Custom Bot",
437
avatar_url="https://example.com/avatar.png"
438
)
439
440
# Send message with embed
441
embed = discord.Embed(
442
title="Webhook Embed",
443
description="This is an embed sent via webhook",
444
color=0x00ff00
445
)
446
embed.add_field(name="Field 1", value="Value 1", inline=True)
447
embed.add_field(name="Field 2", value="Value 2", inline=True)
448
embed.set_footer(text="Sent via webhook")
449
450
await webhook.send(embed=embed)
451
452
# Run the async function
453
asyncio.run(send_webhook_message())
454
```
455
456
### Webhook with File Upload
457
458
```python
459
async def send_webhook_with_file():
460
"""Send webhook message with file attachment."""
461
webhook_url = "https://discord.com/api/webhooks/YOUR_WEBHOOK_URL"
462
463
async with aiohttp.ClientSession() as session:
464
webhook = discord.Webhook.from_url(webhook_url, session=session)
465
466
# Send file from disk
467
with open('example.png', 'rb') as f:
468
file = discord.File(f, filename='example.png')
469
await webhook.send("Check out this image!", file=file)
470
471
# Send multiple files
472
files = [
473
discord.File('file1.txt'),
474
discord.File('file2.png', filename='renamed.png')
475
]
476
await webhook.send("Multiple files!", files=files)
477
478
# Send file with embed
479
embed = discord.Embed(title="File Upload")
480
embed.set_image(url="attachment://example.png")
481
482
with open('example.png', 'rb') as f:
483
file = discord.File(f, filename='example.png')
484
await webhook.send(embed=embed, file=file)
485
486
asyncio.run(send_webhook_with_file())
487
```
488
489
### Message Editing and Management
490
491
```python
492
async def manage_webhook_messages():
493
"""Demonstrate webhook message management."""
494
webhook_url = "https://discord.com/api/webhooks/YOUR_WEBHOOK_URL"
495
496
async with aiohttp.ClientSession() as session:
497
webhook = discord.Webhook.from_url(webhook_url, session=session)
498
499
# Send message and get response
500
message = await webhook.send("Initial message", wait=True)
501
print(f"Sent message with ID: {message.id}")
502
503
# Wait a bit
504
await asyncio.sleep(2)
505
506
# Edit the message
507
await webhook.edit_message(
508
message.id,
509
content="Edited message!",
510
embed=discord.Embed(title="Edited", color=0xff9900)
511
)
512
513
# Wait and delete
514
await asyncio.sleep(2)
515
await webhook.delete_message(message.id)
516
print("Message deleted")
517
518
asyncio.run(manage_webhook_messages())
519
```
520
521
### Thread Support
522
523
```python
524
async def webhook_with_threads():
525
"""Use webhooks with Discord threads."""
526
webhook_url = "https://discord.com/api/webhooks/YOUR_WEBHOOK_URL"
527
528
async with aiohttp.ClientSession() as session:
529
webhook = discord.Webhook.from_url(webhook_url, session=session)
530
531
# Send message and create thread
532
message = await webhook.send("Starting a discussion!", wait=True)
533
534
# Create thread from message
535
thread = await webhook.create_thread(
536
name="Discussion Thread",
537
message=message,
538
auto_archive_duration=1440, # 24 hours
539
reason="Starting discussion"
540
)
541
542
# Send messages in the thread
543
await webhook.send(
544
"This is in the thread!",
545
thread=thread.id
546
)
547
548
await webhook.send(
549
"Another thread message with embed!",
550
thread=thread.id,
551
embed=discord.Embed(
552
title="Thread Message",
553
description="This message is in a thread",
554
color=0x9932cc
555
)
556
)
557
558
asyncio.run(webhook_with_threads())
559
```
560
561
### Synchronous Webhook Usage
562
563
```python
564
import discord
565
import requests
566
567
def sync_webhook_example():
568
"""Example using synchronous webhook client."""
569
webhook_url = "https://discord.com/api/webhooks/YOUR_WEBHOOK_URL"
570
571
# Create synchronous webhook
572
webhook = discord.SyncWebhook.from_url(webhook_url)
573
574
# Send simple message
575
webhook.send("Hello from sync webhook!")
576
577
# Send with embed
578
embed = discord.Embed(
579
title="Sync Webhook",
580
description="This was sent synchronously!",
581
color=0x00ff00
582
)
583
584
message = webhook.send(embed=embed, wait=True)
585
print(f"Sent message: {message.id}")
586
587
# Edit the message
588
webhook.edit_message(
589
message.id,
590
content="Edited sync message!",
591
embed=discord.Embed(title="Edited", color=0xff0000)
592
)
593
594
# Call synchronous function
595
sync_webhook_example()
596
```
597
598
### Webhook Error Handling
599
600
```python
601
import discord
602
import aiohttp
603
import asyncio
604
605
async def webhook_with_error_handling():
606
"""Demonstrate proper webhook error handling."""
607
webhook_url = "https://discord.com/api/webhooks/YOUR_WEBHOOK_URL"
608
609
async with aiohttp.ClientSession() as session:
610
webhook = discord.Webhook.from_url(webhook_url, session=session)
611
612
try:
613
# Try to send a message
614
await webhook.send("Test message")
615
print("Message sent successfully")
616
617
except discord.HTTPException as e:
618
if e.status == 404:
619
print("Webhook not found - check URL")
620
elif e.status == 429:
621
print(f"Rate limited - retry after {e.retry_after} seconds")
622
else:
623
print(f"HTTP error: {e.status} - {e.text}")
624
625
except discord.InvalidArgument as e:
626
print(f"Invalid argument: {e}")
627
628
except Exception as e:
629
print(f"Unexpected error: {e}")
630
631
# Validate webhook before using
632
try:
633
# Fetch webhook info to validate
634
webhook_info = await webhook.fetch()
635
print(f"Using webhook: {webhook_info.name}")
636
637
except discord.NotFound:
638
print("Webhook does not exist")
639
return
640
except discord.Forbidden:
641
print("No permission to access webhook")
642
return
643
644
# Send message with validation
645
if len("Very long message content" * 100) > 2000:
646
print("Message too long, truncating...")
647
content = ("Very long message content" * 100)[:1997] + "..."
648
else:
649
content = "Normal message"
650
651
await webhook.send(content)
652
653
asyncio.run(webhook_with_error_handling())
654
```
655
656
### Advanced Webhook Bot
657
658
```python
659
import discord
660
import aiohttp
661
import asyncio
662
from datetime import datetime, timezone
663
664
class WebhookBot:
665
"""Advanced webhook bot with multiple features."""
666
667
def __init__(self, webhook_url: str):
668
self.webhook_url = webhook_url
669
self.session = None
670
self.webhook = None
671
672
async def __aenter__(self):
673
self.session = aiohttp.ClientSession()
674
self.webhook = discord.Webhook.from_url(self.webhook_url, session=self.session)
675
return self
676
677
async def __aexit__(self, exc_type, exc_val, exc_tb):
678
if self.session:
679
await self.session.close()
680
681
async def send_notification(self, title: str, message: str, color: int = 0x0099ff):
682
"""Send a notification embed."""
683
embed = discord.Embed(
684
title=title,
685
description=message,
686
color=color,
687
timestamp=datetime.now(timezone.utc)
688
)
689
embed.set_footer(text="Notification System")
690
691
return await self.webhook.send(embed=embed, wait=True)
692
693
async def send_status_update(self, service: str, status: str, details: str = None):
694
"""Send a service status update."""
695
status_colors = {
696
"online": 0x00ff00,
697
"warning": 0xffaa00,
698
"offline": 0xff0000
699
}
700
701
embed = discord.Embed(
702
title=f"π§ {service} Status Update",
703
color=status_colors.get(status.lower(), 0x666666),
704
timestamp=datetime.now(timezone.utc)
705
)
706
707
embed.add_field(name="Status", value=status.title(), inline=True)
708
embed.add_field(name="Service", value=service, inline=True)
709
embed.add_field(name="Time", value=f"<t:{int(datetime.now().timestamp())}:R>", inline=True)
710
711
if details:
712
embed.add_field(name="Details", value=details, inline=False)
713
714
return await self.webhook.send(embed=embed, wait=True)
715
716
async def send_log_message(self, level: str, message: str, extra_data: dict = None):
717
"""Send a log message with formatting."""
718
level_colors = {
719
"debug": 0x666666,
720
"info": 0x0099ff,
721
"warning": 0xffaa00,
722
"error": 0xff0000,
723
"critical": 0x990000
724
}
725
726
level_emojis = {
727
"debug": "π",
728
"info": "βΉοΈ",
729
"warning": "β οΈ",
730
"error": "β",
731
"critical": "π¨"
732
}
733
734
embed = discord.Embed(
735
title=f"{level_emojis.get(level.lower(), 'π')} {level.upper()}",
736
description=f"```\n{message}\n```",
737
color=level_colors.get(level.lower(), 0x666666),
738
timestamp=datetime.now(timezone.utc)
739
)
740
741
if extra_data:
742
for key, value in extra_data.items():
743
embed.add_field(name=key.title(), value=str(value), inline=True)
744
745
return await self.webhook.send(embed=embed, wait=True)
746
747
async def send_metrics(self, metrics: dict):
748
"""Send system metrics."""
749
embed = discord.Embed(
750
title="π System Metrics",
751
color=0x9932cc,
752
timestamp=datetime.now(timezone.utc)
753
)
754
755
for metric, value in metrics.items():
756
embed.add_field(name=metric.replace('_', ' ').title(), value=value, inline=True)
757
758
return await self.webhook.send(embed=embed, wait=True)
759
760
# Usage example
761
async def main():
762
webhook_url = "https://discord.com/api/webhooks/YOUR_WEBHOOK_URL"
763
764
async with WebhookBot(webhook_url) as bot:
765
# Send various types of messages
766
await bot.send_notification(
767
"System Alert",
768
"Database connection restored",
769
color=0x00ff00
770
)
771
772
await bot.send_status_update(
773
"Web Server",
774
"online",
775
"All systems operational"
776
)
777
778
await bot.send_log_message(
779
"error",
780
"Failed to process user request",
781
{"user_id": 12345, "endpoint": "/api/users", "error_code": 500}
782
)
783
784
await bot.send_metrics({
785
"cpu_usage": "45%",
786
"memory_usage": "2.1GB / 8GB",
787
"active_users": 1234,
788
"requests_per_minute": 89
789
})
790
791
if __name__ == "__main__":
792
asyncio.run(main())
793
```