Async, idiomatic Rust client for the Assinafy electronic-signature API.
The SDK is a 1:1 mapping of the public REST surface documented at https://api.assinafy.com.br/v1/docs:
| Surface | Module |
|---|---|
| Authentication | Client::auth_api |
| API keys | Client::api_keys |
| Signers | Client::signers |
| Signer (self) | Client::signer_self |
| Documents | Client::documents |
| Assignments | Client::assignments |
| Tags | Client::tags |
| Fields | Client::fields |
| Templates | Client::templates |
| Webhooks | Client::webhooks |
| Activities | Client::activities |
| Public endpoints | Client::public |
Requires Rust 1.86 or newer.
[dependencies]
assinafy = "0.1"
tokio = { version = "1", features = ["macros", "rt-multi-thread"] }use assinafy::Client;
# async fn run() -> assinafy::Result<()> {
let client = Client::builder()
.api_key(std::env::var("ASSINAFY_API_KEY").unwrap())
.sandbox() // omit for production
.build()?;
let signers = client
.signers("102d25a489f34a275d31a16045fd")
.list()
.per_page(50)
.send()
.await?;
for s in &signers.data {
println!("{} <{:?}>", s.full_name, s.email);
}
# Ok(()) }use assinafy::{Auth, Client};
// Server-to-server via API key (default for most users).
let c1 = Client::builder().api_key("...").build().unwrap();
// User-token flow.
let c2 = Client::builder()
.bearer("eyJhbGciOi...")
.build()
.unwrap();
// Query-parameter access-token flow, when required by an integration.
let c2_query = Client::builder()
.access_token("eyJhbGciOi...")
.build()
.unwrap();
// Signer-facing endpoints use the URL access code.
let c3 = c1.with_auth(Auth::AccessCode("signer-token".into()));use assinafy::Client;
use assinafy::resources::UploadDocumentRequest;
# async fn run() -> assinafy::Result<()> {
let client = Client::from_api_key("k")?;
let upload = UploadDocumentRequest::from_path("./contract.pdf").await?;
let doc = client.documents().upload("acc_123", upload).await?;
println!("uploaded {} ({})", doc.name, doc.id);
# Ok(()) }use assinafy::Client;
use assinafy::models::AssignmentMethod;
use assinafy::resources::CreateAssignmentBody;
# async fn run() -> assinafy::Result<()> {
let client = Client::from_api_key("k")?;
let body = CreateAssignmentBody::new(
AssignmentMethod::Virtual,
["sig_1", "sig_2"],
)
.message("Please sign by Friday.");
let assignment = client.assignments().create("doc_abc", &body).await?;
// Clear expiration by sending the documented JSON null value.
client
.assignments()
.reset_expiration("doc_abc", &assignment.id, None)
.await?;
for url in &assignment.signing_urls {
println!("signer {} -> {}", url.signer_id, url.url);
}
# Ok(()) }use assinafy::Client;
use assinafy::models::VerificationMethod;
use assinafy::resources::{CreateDocumentFromTemplateBody, TemplateDocumentSigner};
# async fn run() -> assinafy::Result<()> {
let client = Client::from_api_key("k")?;
let estimate_body = CreateDocumentFromTemplateBody::default()
.signers(vec![
TemplateDocumentSigner::role("role_123")
.verification_method(VerificationMethod::Whatsapp),
]);
let _cost = client
.templates("acc_123")
.estimate_cost("tmpl_abc", &estimate_body)
.await?;
# Ok(()) }use assinafy::Client;
use assinafy::resources::CreateTagBody;
# async fn run() -> assinafy::Result<()> {
let client = Client::from_api_key("k")?;
let tag = client
.tags("acc_123")
.create(&CreateTagBody::new("Contracts").color("3399ff"))
.await?;
println!("tag id: {}", tag.id);
# Ok(()) }Document tag operations use tag names, matching the API reference:
use assinafy::Client;
# async fn run() -> assinafy::Result<()> {
let client = Client::from_api_key("k")?;
let tags = client.tags("acc_123");
tags.add_to_document("doc_abc", ["Contracts", "Urgent"]).await?;
tags.set_on_document("doc_abc", ["Signed"]).await?;
# Ok(()) }Signer-facing endpoints use Auth::AccessCode, which automatically adds the
signer-access-code query parameter to every request:
use assinafy::{Auth, Client};
use assinafy::resources::{ConfirmSignerDataBody, VerifyCodeBody};
# async fn run() -> assinafy::Result<()> {
let client = Client::from_api_key("k")?
.with_auth(Auth::AccessCode("signer-access-code".into()));
let signer = client.signer_self().me().await?;
client.signer_self().verify(&VerifyCodeBody::new("123456")).await?;
client
.signer_self()
.confirm_data(
"doc_abc",
&ConfirmSignerDataBody::new()
.email("signer@example.com")
.accepted_terms(true),
)
.await?;
println!("ready signer {}", signer.id);
# Ok(()) }Every paged endpoint returns a Page<T>
containing data and meta (extracted from the X-Pagination-* response
headers):
use assinafy::Client;
# async fn run() -> assinafy::Result<()> {
let client = Client::from_api_key("k")?;
let mut next = Some(1);
while let Some(page) = next {
let res = client.signers("acc_123").list().page(page).per_page(100).send().await?;
println!("page {page}: {} items", res.data.len());
next = res.next_page();
}
# Ok(()) }All operations return Result<T, Error>.
API errors retain the HTTP status, server message and raw error payload:
use assinafy::{Client, Error};
# async fn run() -> assinafy::Result<()> {
let client = Client::from_api_key("k")?;
match client.signers("acc_123").get("missing").await {
Ok(_) => {}
Err(Error::Api(e)) if e.status == 404 => eprintln!("not found"),
Err(other) => return Err(other),
}
# Ok(()) }Use [ClientBuilder::sandbox] to target the public sandbox at
https://sandbox.assinafy.com.br/v1.
rustls-tls(default) — TLS via rustls.native-tls— TLS via the operating system's native stack.
export ASSINAFY_API_KEY=<sandbox-key>
export ASSINAFY_ACCOUNT_ID=<sandbox-account>
cargo test --test sandbox -- --ignoredThe --ignored flag is required because the tests hit the live sandbox; CI
runs them on a schedule.
See AUDIT.md for the endpoint coverage and verification audit.
Dual-licensed under MIT or Apache-2.0 at your option.