Receitas completas de integração
Guias · v1 · Produção · Revisado em · Markdown
Use um backend com chave limitada. Os clientes de exemplo JavaScript e Python usam homologação por padrão; para produção, selecione explicitamente environment: "production" no JavaScript ou environment="production" no Python. Incluem timeout, até três tentativas com a mesma intenção, leitura de Retry-After, paginação e consulta de operações. São exemplos, não SDKs com garantia de estabilidade. Nenhuma chamada ocorre ao importar. Salve os arquivos no seu servidor e defina AGILINK_API_KEY no ambiente seguro.
Todos os 143 endpoints têm cURL, JS/TS e Python na referência por operação. Os exemplos usam IDs fictícios; substitua-os pelos recursos autorizados. Downloads são autenticados. Nunca registre o segredo de uma chave ou webhook em logs.
Criar app, domínio e primeiro link
Seção intitulada “Criar app, domínio e primeiro link”Criar app exige sessão humana verificada. Obtenha-a pelo login do produto, mantenha-a no servidor seguro e não use credencial administrativa de provedor. Conectar domínio depende da capacidade e autorização do app. Aguardar operação não comprova associação nativa em aparelho.
import {AgilinkClient} from './agilink-client.mjs';const human = new AgilinkClient(process.env.AGILINK_HUMAN_SESSION);const {data: created} = await human.request('/apps', {method:'POST',body:{name:'Minha loja'}});const appId = created.data.id;const {data: setup} = await human.request('/domains', {method:'POST',body:{appId,mode:'platform',prefix:'minha-loja'}});const connected = await human.waitOperation(setup.data.id);if (connected.status !== 'succeeded') throw Error('Confira o domínio antes de continuar.');const domains = await human.request('/domains?appId='+encodeURIComponent(appId));const domain = domains.data.data.find(d=>d.links==='active' && !d.detached);if (!domain) throw Error('Aguarde propriedade, DNS e HTTPS.');const {data: link} = await human.request('/links', {method:'POST',body:{appId,domain:domain.hostname,slug:'produto',desktopUrl:'https://shop.example.com/produto',webOnly:true}});const publication = await human.request('/links/'+encodeURIComponent(link.data.id)+'/publication');// Confira publication.data.data; a propagação pública é eventual.import osfrom agilink_client import AgilinkClienthuman = AgilinkClient(os.environ['AGILINK_HUMAN_SESSION'])app_id = human.request('/apps', method='POST', body={'name':'Minha loja'})['data']['data']['id']setup = human.request('/domains', method='POST', body={'appId':app_id,'mode':'platform','prefix':'minha-loja'})state = human.wait_operation(setup['data']['data']['id'])if state['status'] != 'succeeded': raise RuntimeError('Confira o domínio antes de continuar')domains = human.request('/domains?appId='+app_id)['data']['data']domain = next((d for d in domains if d['links'] == 'active' and not d['detached']), None)if domain is None: raise RuntimeError('Aguarde propriedade, DNS e HTTPS')link = human.request('/links', method='POST', body={'appId':app_id,'domain':domain['hostname'],'slug':'produto','desktopUrl':'https://shop.example.com/produto','webOnly':True})from urllib.parse import quotepublication = human.request('/links/'+quote(link['data']['data']['id'], safe='')+'/publication')Aplicativos · Domínios · Abrir e medir.
Listar com filtros e cursor
Seção intitulada “Listar com filtros e cursor”import {AgilinkClient} from './agilink-client.mjs';const api = new AgilinkClient();for await (const links of api.pages('/links?appId=app_loja&group=operational&q=produto')) { for (const link of links) console.log(link.id, link.title);}from agilink_client import AgilinkClientapi = AgilinkClient()for links in api.pages('/links?appId=app_loja&group=operational&q=produto'): for link in links: print(link['id'], link['title'])Mantenha credencial, app, filtros, ordenação e limite; cursor expira em 15 minutos. A lista pode mudar entre páginas. Para arquivo limitado na execução, use exportação.
Editar com ETag e reconciliar conflito
Seção intitulada “Editar com ETag e reconciliar conflito”import {AgilinkClient, AgilinkError} from './agilink-client.mjs';const api = new AgilinkClient(), path = '/links/'+encodeURIComponent('go.example.com~-ofertas~-tenis');const current = await api.request(path);try { await api.request(path,{method:'PATCH',revision:current.data.data.revision,body:{title:'Título revisado'}}); }catch (error) { if (!(error instanceof AgilinkError) || error.status !== 412) throw error; const latest = await api.request(path); // Mostre latest e a intenção ao usuário; reconcilie antes de criar uma NOVA edição.}from agilink_client import AgilinkClient, AgilinkErrorapi = AgilinkClient()path = '/links/go.example.com~-ofertas~-tenis'current = api.request(path)try: api.request(path, method='PATCH', revision=current['data']['data']['revision'], body={'title':'Título revisado'})except AgilinkError as error: if error.status != 412: raise latest = api.request(path) # Reconciliar com a pessoa antes de nova intenção; não sobrescrever cegamente.Timeout sem duplicar criação
Seção intitulada “Timeout sem duplicar criação”Gere um UUID por intenção e preserve corpo, caminho, revisão e credencial em todas as tentativas. Os clientes de exemplo conservam esses valores nas até três tentativas. Se esgotar, guarde error.intentId (JS) / error.intent_id (Python) e consulte o recurso antes de decidir repetir. Para emissão de segredo, 409 secret_not_replayable exige consultar/revogar o recurso identificado; não gerar nova chave automaticamente.
import {AgilinkClient} from './agilink-client.mjs';const api = new AgilinkClient(), intentId = crypto.randomUUID();const body = {appId:'app_loja',domain:'go.example.com',slug:'produto',desktopUrl:'https://shop.example.com/produto'};const result = await api.request('/links',{method:'POST',body,intentId});// Em resultado incerto, preserve intentId e body; não chame randomUUID para o mesmo retry.Gerar QR e baixar
Seção intitulada “Gerar QR e baixar”import {writeFile} from 'node:fs/promises';import {AgilinkClient} from './agilink-client.mjs';const api = new AgilinkClient();const {data: saved} = await api.request('/qr-codes',{method:'POST',body:{appId:'app_loja',linkId:'go.example.com~-ofertas~-tenis',name:'Vitrine'}});const image = await api.request('/qr-codes/'+saved.data.id+'/download?format=png',{binary:true});await writeFile('vitrine.png',new Uint8Array(image.data));from agilink_client import AgilinkClientapi = AgilinkClient()saved = api.request('/qr-codes', method='POST', body={'appId':'app_loja','linkId':'go.example.com~-ofertas~-tenis','name':'Vitrine'})image = api.request('/qr-codes/'+saved['data']['data']['id']+'/download?format=png', binary=True)with open('vitrine.png','wb') as output: output.write(image['data'])Confira câmera e impressão em aparelho real. Limites do QR.
Criar campanha e consultar métricas
Seção intitulada “Criar campanha e consultar métricas”POST /campaigns com appId/name/utm → GET /links/{linkId} → PATCH com campaignId e cópia explícita de utm usando revisão → GET /analytics/campaigns/{campaignId}?start=…&end=…. Copiar UTMs não sincroniza alterações futuras. Métricas seguem associações atuais e cobertura dos eventos retidos.
import {AgilinkClient} from './agilink-client.mjs';const api = new AgilinkClient();const campaign = (await api.request('/campaigns',{method:'POST',body:{appId:'app_loja',name:'Verão',utm:{utm_campaign:'verao'}}})).data.data;const path = '/links/'+encodeURIComponent('go.example.com~-ofertas~-tenis');const link = (await api.request(path)).data.data;await api.request(path,{method:'PATCH',revision:link.revision,body:{campaignId:campaign.id,utm:campaign.utm}});const metric = await api.request('/analytics/campaigns/'+campaign.id+'?start=2026-10-01T00%3A00%3A00Z&end=2026-11-01T00%3A00%3A00Z');Publicar template e restaurar
Seção intitulada “Publicar template e restaurar”GET /template-catalog?appId=… → POST /templates com modelo disponível → PUT /templates/{id}/draft com revisão e variáveis válidas → POST /templates/{id}/validate → POST /templates/{id}/versions para snapshot imutável → POST /templates/{id}/publish → GET /template-publications/{id} até confirmação ou falha → POST /templates/{id}/restore para versão anterior.
A publicação usa schema próprio de intenção/alvos; copie os requests validados da referência, não um objeto arbitrário. Antes de restaurar, consulte a revisão atual; restauração é nova intenção e não reescreve snapshots. Requests completos e alvos · Herança e editor.
Importar, corrigir e confirmar
Seção intitulada “Importar, corrigir e confirmar”import {AgilinkClient} from './agilink-client.mjs';const api = new AgilinkClient();const uploaded = (await api.request('/imports',{method:'POST',body:{appId:'app_loja',format:'csv',content:'domain,slug,desktopUrl\ngo.example.com,produto,https://shop.example.com/produto'}})).data.data;const checked = (await api.request('/imports/'+uploaded.id+'/validate',{method:'POST',body:{}})).data.data;const rows = (await api.request('/imports/'+uploaded.id+'/items')).data.data;if (checked.failed) throw Error('Confira rows; corrija e envie apenas linhas necessárias em nova importação.');const queued = (await api.request('/imports/'+uploaded.id+'/commit',{method:'POST',revision:checked.revision,body:{}})).data.data;const outcome = await api.waitOperation(queued.operationId);// Consulte itens novamente; parcialmente concluído não significa rollback de todos.from agilink_client import AgilinkClientapi = AgilinkClient()uploaded = api.request('/imports',method='POST',body={'appId':'app_loja','format':'json','content':'[{"domain":"go.example.com","slug":"produto","desktopUrl":"https://shop.example.com/produto"}]'})['data']['data']checked = api.request('/imports/'+uploaded['id']+'/validate',method='POST',body={})['data']['data']if checked['failed']: raise RuntimeError('Consulte os itens e corrija as linhas')queued = api.request('/imports/'+uploaded['id']+'/commit',method='POST',revision=checked['revision'],body={})['data']['data']outcome = api.wait_operation(queued['operationId'])Exportar e baixar antes de expirar
Seção intitulada “Exportar e baixar antes de expirar”import {writeFile} from 'node:fs/promises';import {AgilinkClient} from './agilink-client.mjs';const api = new AgilinkClient();const queued = (await api.request('/exports',{method:'POST',body:{appId:'app_loja',dataset:'links',format:'csv'}})).data.data;const operation = await api.waitOperation(queued.operationId);if(operation.status!=='succeeded') throw Error('Confira limite, autorização e erro da operação.');const file = await api.request('/exports/'+queued.id+'/download',{binary:true});await writeFile('links.csv',new Uint8Array(file.data));from agilink_client import AgilinkClientapi = AgilinkClient()queued = api.request('/exports',method='POST',body={'appId':'app_loja','dataset':'links','format':'csv'})['data']['data']operation = api.wait_operation(queued['operationId'])if operation['status'] != 'succeeded': raise RuntimeError('Confira limite, autorização e erro')with open('links.csv','wb') as output: output.write(api.request('/exports/'+queued['id']+'/download',binary=True)['data'])Máximo 1.000 registros ou 1 MiB. Arquivo por 24h; download exige leitura atual do conjunto original. CSV neutraliza fórmulas: preserve essa proteção.
Receber webhook, verificar e deduplicar
Seção intitulada “Receber webhook, verificar e deduplicar”- Cadastre seu endpoint HTTPS público IPv4/443 em POST /webhooks, eventos e app. Guarde o segredo uma vez no servidor.
- Receba corpo bruto; valide Agilink-Signature com timestamp até 300s e HMAC-SHA256 antes de interpretar o JSON.
- Faça deduplicação durável por event.id em transação antes de aplicar efeito; responda 2xx rapidamente e coloque trabalho longo na sua fila.
- Use POST /webhooks/{id}/test; confira test:true e histórico. Timeout/3xx/5xx têm retentativas; não há ordem global.
- Para mudar segredo, rotacione com revisão e sobreposição, atualize receptor e só depois retire o anterior. Destino alterado cancela pendências da revisão antiga; reentrega é explícita.
Verificador HMAC completo e política de retentativas. Não exponha esse segredo no mobile ou no portal público.
Integrar Android, iOS e Flutter
Seção intitulada “Integrar Android, iOS e Flutter”Configure a associação de domínio, pacote Android/certificados e bundle iOS/team conforme sua distribuição; configure appPath relativo. Instale o SDK Flutter, trate as entradas HTTPS/scheme/colagem e registre abertura/conteúdo com IDs estáveis. App Links/Universal Links e recuperação pós-instalação são etapas distintas. Faça os testes em aparelho real pelas lojas, incluindo sem app instalado, instalado, link inválido, repetição e ausência de resultado elegível. Recebimento e parâmetros · Recuperação.
// Dentro do seu handler de URI, depois de receber pelo fluxo configurado no SDK:void tratarUri(Uri uri) { final produto = uri.queryParameters['produto']; if (produto == null || produto.isEmpty) return; // Valide o produto no backend do aplicativo antes de navegar.}O exemplo Dart é tratamento de parâmetro, não uma API inventada do SDK. Use as assinaturas do pacote e o guia Flutter da versão integrada.
Rotacionar/revogar e interpretar 401/403
Seção intitulada “Rotacionar/revogar e interpretar 401/403”Na área Desenvolvedores, selecione chave, revise alcance/prazo e rotacione. Via API humana: GET /api-keys/{id} → POST /api-keys/{id}/rotate com If-Match e overlapSeconds → guarde novo segredo sem log → atualize consumidor → teste leitura autorizada → DELETE da anterior se ainda vigente.
401 indica credencial/sessão/validade/revogação; 403 indica permissão/capacidade; não resolva ambos emitindo uma chave mais ampla automaticamente. Para reduzir, PATCH com subconjunto de apps/permissões/prazo. Requests humanos completos · Diagnóstico.