| Crates.io | clickup |
| lib.rs | clickup |
| version | 0.3.0 |
| created_at | 2025-11-07 13:46:38.615634+00 |
| updated_at | 2025-11-17 12:29:20.704479+00 |
| description | Cliente completo da API ClickUp com funcionalidades avançadas (smart search, fuzzy matching) |
| homepage | https://github.com/nextlw/api_clickup |
| repository | https://github.com/nextlw/api_clickup |
| max_upload_size | |
| id | 1921615 |
| size | 228,895 |
Cliente completo e tipo-seguro para a API do ClickUp, com funcionalidades avançadas de busca inteligente (fuzzy matching) e tipos estruturados.
thiserrortracingAdicione ao Cargo.toml:
[dependencies]
clickup = { path = "crates/clickup" }
NUNCA hardcode tokens ou IDs no código! Use variáveis de ambiente:
# Configure as variáveis de ambiente
export CLICKUP_API_TOKEN="pk_your_token_here"
export CLICKUP_WORKSPACE_ID="your_workspace_id"
use clickup::ClickUpClient;
#[tokio::main]
async fn main() -> clickup::Result<()> {
// Ler token de variável de ambiente (OBRIGATÓRIO)
let api_token = std::env::var("CLICKUP_API_TOKEN")
.expect("CLICKUP_API_TOKEN não configurado");
let client = ClickUpClient::new(api_token)?;
Ok(())
}
use clickup::{Task, Priority};
use clickup::tasks::TaskManager;
let client = ClickUpClient::new(api_token)?;
let task_manager = TaskManager::new(client, Some("list_123".to_string()));
// Criar task usando builder pattern
let task = Task::new("Nova tarefa")
.with_description("Descrição detalhada")
.with_priority(Priority::High)
.with_list_id("list_123");
// Criar no ClickUp
let created_task = task_manager.create_task(&task).await?;
println!("Task criada: {}", created_task.id.unwrap());
// Atribuir usuário
let task = task_manager.assign_task("task_id", vec![12345]).await?;
// Atualizar status
let task = task_manager.update_task_status("task_id", "em progresso").await?;
// Criar subtask
let subtask = Task::new("Subtarefa")
.with_parent("parent_task_id");
let created_subtask = task_manager.create_subtask("parent_task_id", &subtask).await?;
// Definir due date
let due_date_ms = 1730073600000i64; // Unix timestamp em milissegundos
let task = task_manager.set_due_date("task_id", due_date_ms).await?;
// Adicionar dependência
task_manager.add_dependency("task_id", "depends_on_task_id", "waiting_on").await?;
use clickup::folders::SmartFolderFinder;
let api_token = std::env::var("CLICKUP_API_TOKEN")
.expect("CLICKUP_API_TOKEN não configurado");
let workspace_id = std::env::var("CLICKUP_WORKSPACE_ID")
.expect("CLICKUP_WORKSPACE_ID não configurado");
let mut finder = SmartFolderFinder::from_token(api_token, workspace_id)?;
let result = finder.find_folder_for_client("Nexcode").await?;
if let Some(folder) = result {
println!("Folder: {} (id: {})", folder.folder_name, folder.folder_id);
println!("Confidence: {:.2}", folder.confidence);
}
use clickup::assignees::SmartAssigneeFinder;
let mut finder = SmartAssigneeFinder::from_token(api_token, workspace_id)?;
let result = finder.find_assignee_by_name("William").await?;
if let Some(assignee) = result {
println!("User: {} (id: {})", assignee.username, assignee.user_id);
}
use clickup::fields::CustomFieldManager;
use clickup::{CustomField, CustomFieldValue};
let manager = CustomFieldManager::from_token(api_token)?;
// Garantir que opção existe no dropdown
let custom_field = manager
.ensure_client_solicitante_option("list_123", "Nexcode")
.await?;
// Criar custom fields tipados
let checkbox = CustomField::checkbox("field_id", true);
let date = CustomField::date("field_id", 1730073600000i64); // milissegundos
let text = CustomField::text("field_id", "Valor texto");
use clickup::webhooks::{WebhookManager, WebhookConfig, WebhookEvent};
let manager = WebhookManager::from_token(api_token, workspace_id)?;
// Criar webhook para receber eventos
let config = WebhookConfig {
endpoint: "https://myapp.com/webhooks/clickup".to_string(),
events: vec![
WebhookEvent::TaskCreated,
WebhookEvent::TaskUpdated,
WebhookEvent::TaskStatusUpdated,
],
status: Some("active".to_string()),
};
let webhook = manager.create_webhook(&config).await?;
println!("Webhook criado: {}", webhook.id);
// Listar webhooks
let webhooks = manager.list_webhooks().await?;
// Criar ou atualizar (idempotente)
let webhook = manager.ensure_webhook(&config).await?;
// Validar assinatura (segurança)
use clickup::webhooks::WebhookPayload;
let is_valid = WebhookPayload::verify_signature(
&signature_header,
&webhook_secret,
&request_body_bytes
);
Combine webhooks ClickUp com Google Cloud Pub/Sub para escalabilidade:
ClickUp → Webhook Handler → Pub/Sub Topic → Workers
↓ valida assinatura
↓ ACK < 100ms
✓ publicado
Eventos Disponíveis (30+ eventos):
Created, Updated, Deleted, Moved, StatusUpdated, PriorityUpdatedCreated, Updated, DeletedCreated, Updated, DeletedCreated, Updated, DeletedCreated, Updated, DeletedVer WebhookEvent enum para lista completa.
crates/clickup/src/
├── client.rs # Cliente HTTP híbrido v2+v3
├── error.rs # Tipos de erro (ClickUpError)
├── matching.rs # Fuzzy matching utilities (Jaro-Winkler)
├── folders.rs # SmartFolderFinder (588 linhas)
├── assignees.rs # SmartAssigneeFinder (340 linhas)
├── fields.rs # CustomFieldManager (302 linhas)
├── tasks.rs # TaskManager - CRUD completo (800+ linhas)
├── webhooks.rs # WebhookManager - create, list, update, delete (400+ linhas)
├── types/ # Tipos estruturados (1,400 linhas)
│ ├── mod.rs # Re-exports
│ ├── priority.rs # Priority enum (1-4)
│ ├── status.rs # Status struct
│ ├── user.rs # User struct
│ ├── custom_field.rs # 18 tipos de custom fields
│ └── task.rs # Task struct + builder
└── lib.rs # Re-exports públicos
Este crate utiliza exclusivamente a API v2 do ClickUp que é estável e completa:
pub struct ClickUpClient {
http_client: HttpClient,
api_token: String,
base_url: String, // "https://api.clickup.com/api/v2"
}
Métodos disponíveis:
get_json(endpoint) - Requisição GET com JSONpost_json(endpoint, body) - Requisição POST com JSONput_json(endpoint, body) - Requisição PUT com JSONdelete_json(endpoint) - Requisição DELETE com JSON| Recurso | Endpoint API v2 |
|---|---|
| Spaces | /team/{team_id}/space |
| Folders | /space/{space_id}/folder |
| Lists | /folder/{folder_id}/list |
| Tasks | /list/{list_id}/task |
| Webhooks | /team/{team_id}/webhook |
| Custom Fields | /list/{list_id}/field |
Interno (código):
let workspace_id = "9013037641"; // workspace_id usado internamente
API calls:
// Internamente: workspace_id = "9013037641"
// Na API v2: /team/9013037641/space
let endpoint = format!("/team/{}/space", workspace_id);
pub enum Priority {
Urgent = 1, // ⚠️ Urgente
High = 2, // 🔴 Alta
Normal = 3, // 🟡 Normal (default)
Low = 4, // 🟢 Baixa
}
pub enum CustomFieldValue {
Text(String),
Number(f64),
Checkbox(String), // ⚠️ CRÍTICO: "true"/"false", NÃO bool!
Dropdown(String),
Labels(Vec<String>),
Date(i64), // ⚠️ CRÍTICO: milissegundos, NÃO segundos!
Users(Vec<u32>),
Phone(String),
Email(String),
Url(String),
Currency(f64),
Rating(u8),
Location(String),
Attachment(String),
// ... mais 4 tipos
}
Atenção:
"true"/"false", não booleanlet task = Task::new("Título da tarefa")
.with_description("Descrição")
.with_list_id("list_123")
.with_priority(Priority::High)
.with_assignees(vec![User { id: 12345, username: "william".to_string() }])
.with_due_date(1730073600000i64)
.with_parent("parent_task_id") // Para subtasks
.with_custom_fields(vec![
CustomField::checkbox("field_id", true),
CustomField::text("field_id2", "Valor"),
]);
# Testes do crate
cargo test -p clickup
# Com output detalhado
cargo test -p clickup -- --nocapture
# Teste específico
cargo test -p clickup test_normalize_name
# Testes de integração
cargo test --test test_assignee_finder
| Módulo | Status | Linhas | Descrição |
|---|---|---|---|
| ✅ client | Completo | 293 | Cliente HTTP v2+v3 |
| ✅ error | Completo | 42 | Tipos de erro |
| ✅ matching | Completo | 164 | Fuzzy matching |
| ✅ folders | Completo | 588 | Smart folder finder |
| ✅ assignees | Completo | 340 | Smart assignee finder |
| ✅ fields | Completo | 302 | Custom field manager |
| ✅ tasks | Completo | 800+ | Task CRUD + features |
| ✅ webhooks | Completo | 400+ | Webhook management (create, list, update, delete) |
| ✅ types | Completo | 1,400 | Task, Priority, Status, CustomField |
Total: ~4,300 linhas de código Rust
# Token de autenticação ClickUp (OBRIGATÓRIO)
export CLICKUP_API_TOKEN="seu_token_aqui"
# ID do Workspace (OBRIGATÓRIO)
export CLICKUP_WORKSPACE_ID="seu_workspace_id_aqui"
# Fallback para código legado
export CLICKUP_TEAM_ID="seu_workspace_id" # Mesmo valor, nome antigo
CLICKUP_API_TOKEN:
CLICKUP_WORKSPACE_ID:
https://app.clickup.com/<WORKSPACE_ID>/...GET https://api.clickup.com/api/v2/team# Armazenar secrets no GCP
gcloud secrets create clickup-api-token --data-file=- <<< "seu_token_aqui"
gcloud secrets create clickup-workspace-id --data-file=- <<< "seu_workspace_id"
# Usar no Cloud Run/Functions
gcloud run deploy ... --set-secrets=CLICKUP_API_TOKEN=clickup-api-token:latest
Este é um crate interno para o projeto ChatGuru-ClickUp middleware.
workspace_id (não team_id)Result<T> com tipos específicosPropriedade da eLai Integration Team / Nordja.