---
name: imap_save_draft
kind: function
lang: py
domain: infra
version: "1.0.0"
purity: impure
signature: "def imap_save_draft(conn, raw_rfc822: bytes, mailbox: str = '[Gmail]/Drafts', flags: str = '\\Draft') -> dict"
description: "Guarda un borrador en un mailbox via IMAP APPEND sobre una conexion imaplib.IMAP4_SSL ya autenticada. Ejecuta conn.append(mailbox, flags, imaplib.Time2Internaldate(time.time()), raw_rfc822): raw_rfc822 son los bytes MIME ya serializados de un email completo (cabeceras + cuerpo) que el caller arma con email.message.EmailMessage().as_bytes() (stdlib) o con las funciones email_build_*_py_infra del registry + serializacion. A diferencia de las demas operaciones del grupo, APPEND NO requiere un mailbox seleccionado: el destino es el argumento mailbox (default '[Gmail]/Drafts', con su prefijo [Gmail]/). flags default '\\Draft' para que el cliente lo trate como borrador. Valida que raw_rfc822 sean bytes. No abre la conexion ni resuelve credenciales. Nunca lanza: devuelve {status:'ok', mailbox} o {status:'error', error}; tambien error si APPEND responde un typ distinto de OK. Parte del grupo email/imap. Solo stdlib (imaplib, time, email.message para construir el MIME)."
tags: [email, imap, mail, draft, append, infra]
uses_functions: []
uses_types: []
returns: []
returns_optional: false
error_type: "error_py_core"
imports: [imaplib, time]
params:
  - name: conn
    desc: "objeto imaplib.IMAP4_SSL (o IMAP4) YA conectado y autenticado. Normalmente lo produce imap_connect. APPEND no requiere mailbox seleccionado: el destino se pasa en el argumento mailbox. La funcion no abre ni cierra conn."
  - name: raw_rfc822
    desc: "bytes MIME ya serializados de un email completo (cabeceras From/To/Subject + cuerpo). El caller los construye con email.message.EmailMessage(...).as_bytes() (stdlib) o con email_build_*_py_infra del registry + serializacion. Debe ser bytes; un str devuelve {status:'error'}."
  - name: mailbox
    desc: "mailbox destino del borrador. Default '[Gmail]/Drafts' (carpeta de borradores de Gmail, con prefijo [Gmail]/). En otros servidores suele ser 'Drafts'. Debe existir en el servidor."
  - name: flags
    desc: "banderas IMAP a poner al mensaje, como string separado por espacios. Default '\\Draft' para marcarlo como borrador. Combinable, p.ej. '\\Draft \\Seen'."
output: "dict. En exito: {status:'ok', mailbox:str} reflejando el mailbox donde se guardo el borrador. En error (sin lanzar): {status:'error', error:str}, p.ej. si raw_rfc822 no son bytes o si APPEND responde un typ distinto de OK."
tested: false
tests: []
test_file_path: ""
file_path: "python/functions/infra/imap_save_draft.py"
---

## Ejemplo

```python
import sys
sys.path.insert(0, "python/functions")
from email.message import EmailMessage
from infra.imap_connect import imap_connect
from infra.imap_save_draft import imap_save_draft

# conn ya conectado y autenticado (lo produce imap_connect). APPEND no necesita select().
conn = imap_connect(...)["conn"]  # firma exacta la define imap_connect

# Armar un borrador minimo con stdlib y serializarlo a bytes MIME.
msg = EmailMessage()
msg["From"] = "yo@example.com"
msg["To"] = "destinatario@example.com"
msg["Subject"] = "Propuesta (borrador)"
msg.set_content("Hola, este es un borrador guardado por IMAP APPEND.")
raw = msg.as_bytes()

# Guardarlo en la carpeta de borradores de Gmail (nota el prefijo [Gmail]/).
print(imap_save_draft(conn, raw, mailbox="[Gmail]/Drafts"))
# {'status': 'ok', 'mailbox': '[Gmail]/Drafts'}
```

## Cuando usarla

Cuando quieres dejar un correo a medio escribir guardado en el servidor (no
enviarlo) para retomarlo desde cualquier cliente: el clasico "guardar borrador".
Util para flujos donde un agente prepara una respuesta y la deja en Drafts para
que el humano la revise y envie. Tambien sirve para archivar copias arbitrarias de
mensajes en un mailbox (cambiando `flags`). El envio real es otra cosa: para
enviar usa `smtp_send_py_infra`. Compone con `email_build_*_py_infra` (que
producen el EmailMessage) + serializacion a bytes.

## Gotchas

- **Impura**: escribe un mensaje nuevo en el servidor (consume cuota de la cuenta).
- **raw_rfc822 son BYTES, no str**: el mensaje MIME debe estar ya serializado a
  `bytes` (`EmailMessage().as_bytes()`). Pasar un `str` devuelve `{status:'error'}`.
  La funcion no construye el MIME: solo lo deposita.
- **APPEND no usa mailbox seleccionado**: a diferencia de STORE/COPY/EXPUNGE, el
  destino de APPEND es el argumento `mailbox`, no el mailbox que el caller
  selecciono. No hace falta `conn.select(...)` previo.
- **Prefijo [Gmail]/ y existencia**: en Gmail la carpeta de borradores es
  `[Gmail]/Drafts` (con prefijo). En otros servidores suele ser `Drafts`. El
  mailbox destino debe existir; si no, APPEND falla y se devuelve error.
- **No hay UID estable garantizado en la respuesta**: APPEND crea un mensaje nuevo;
  algunos servidores devuelven su UID (APPENDUID) y otros no. Esta funcion no lo
  parsea — devuelve solo `{status, mailbox}`. Si necesitas el UID del borrador,
  busca despues con `imap_search`.
- **flags con backslash**: las banderas del sistema llevan barra invertida
  (`\\Draft`, `\\Seen`). En el string Python recuerda escaparla (`"\\Draft"`).
- **Nunca lanza**: cualquier fallo (mailbox inexistente, conexion caida, bytes
  invalidos, respuesta no-OK) vuelve como `{status:'error', error:str}`.
