> ## Documentation Index
> Fetch the complete documentation index at: https://docs.fortiseval.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Request a translation

> Order document translations through the API and get notified when they're delivered

export const WebhookDeliveryDiagram = () => <svg style={{
  width: "100%",
  height: "auto",
  display: "block"
}} xmlns="http://www.w3.org/2000/svg" viewBox="0 0 800 330" role="img" aria-labelledby="whd-t whd-d">
    <title id="whd-t">How a Fortis webhook is delivered</title>
    <desc id="whd-d">Fortis signs each event and POSTs it to your endpoint. A failed delivery is retried with backoff; your endpoint verifies the signature, deduplicates on X-Fortis-Delivery, and answers 2xx.</desc>
    <defs>
      <filter id="whd-glow" x="-100%" y="-100%" width="300%" height="300%">
        <feGaussianBlur stdDeviation="3.5" result="b" />
        <feMerge><feMergeNode in="b" /><feMergeNode in="SourceGraphic" /></feMerge>
      </filter>
      <pattern id="whd-dots" width="20" height="20" patternUnits="userSpaceOnUse">
        <circle cx="1" cy="1" r="1" fill="#FFFFFF" opacity="0.045" />
      </pattern>
    </defs>
    <rect width="800" height="330" rx="14" fill="#1C1917" />
    <rect width="800" height="330" rx="14" fill="url(#whd-dots)" />
    
    <text x="32" y="38" fontFamily="ui-sans-serif, -apple-system, BlinkMacSystemFont, 'Segoe UI', Helvetica, Arial, sans-serif" fontSize="12" fontWeight="600" letterSpacing="1.4" fill="#A8A29E">WEBHOOK DELIVERY · SIGN, RETRY, VERIFY</text>
    <rect x="40" y="108" width="170" height="84" rx="12" fill="#292524" stroke="#44403C" />
    <text x="125.0" y="144" textAnchor="middle" fontFamily="ui-sans-serif, -apple-system, BlinkMacSystemFont, 'Segoe UI', Helvetica, Arial, sans-serif" fontSize="15" fontWeight="600" fill="#F5F5F4">Fortis</text>
    <text x="125.0" y="166" textAnchor="middle" fontFamily="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" fontSize="11" fill="#A8A29E">order.completed</text>
    <rect x="590" y="108" width="170" height="84" rx="12" fill="#292524" stroke="#44403C"><animate attributeName="stroke" values="#44403C;#44403C;#F87171;#F87171;#44403C;#44403C;#34D399;#34D399;#44403C;#44403C" keyTimes="0;0.1600;0.1633;0.2200;0.2467;0.6067;0.6133;0.9200;0.9600;1" dur="15s" repeatCount="indefinite" /></rect>
    <text x="675.0" y="144" textAnchor="middle" fontFamily="ui-sans-serif, -apple-system, BlinkMacSystemFont, 'Segoe UI', Helvetica, Arial, sans-serif" fontSize="15" fontWeight="600" fill="#F5F5F4">Your endpoint</text>
    <text x="675.0" y="166" textAnchor="middle" fontFamily="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" fontSize="11" fill="#A8A29E">/webhooks/fortis</text>
    <line x1="210" y1="150" x2="590" y2="150" stroke="#44403C" strokeWidth="2" strokeDasharray="2 6" strokeLinecap="round" />
    <g><animate attributeName="opacity" values="1;1;0;0" keyTimes="0;0.9200;0.9600;1" dur="15s" repeatCount="indefinite" />
    <g opacity="0"><animate attributeName="opacity" values="0;0;1;1" keyTimes="0;0.0200;0.0467;1" dur="15s" repeatCount="indefinite" />
    <rect x="240" y="80" width="320" height="46" rx="8" fill="#292524" fillOpacity="0.7" stroke="#44403C" />
    <text fontFamily="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" fontSize="11" fill="#A8A29E"><tspan x="254" y="97"><tspan fill="#FBBF24">X-Fortis-Signature:</tspan> t=1790000000,v1=5f3a…</tspan><tspan x="254" y="115"><tspan fill="#FBBF24">X-Fortis-Delivery:</tspan>  whreq_8KqL…</tspan></text></g>
    <g opacity="0" filter="url(#whd-glow)"><animateMotion path="M 240 150 L 558 150" keyPoints="0;0;1;1" keyTimes="0;0.0667;0.1600;1" calcMode="linear" dur="15s" repeatCount="indefinite" /><animate attributeName="opacity" values="0;0;1;1;0;0" keyTimes="0;0.0667;0.0733;0.1767;0.1967;1" dur="15s" repeatCount="indefinite" /><g><rect x="-26" y="-17" width="52" height="34" rx="5" fill="#1C1917" stroke="#34D399" strokeWidth="2" /><path d="M -24 -14 L 0 4 L 24 -14" fill="none" stroke="#34D399" strokeWidth="2" strokeLinejoin="round" /><animate attributeName="opacity" values="1;1;1" keyTimes="0;0.1600;1" dur="15s" repeatCount="indefinite" /></g></g>
    <g opacity="0"><animate attributeName="opacity" values="0;0;1;1;0;0" keyTimes="0;0.1633;0.1767;0.3200;0.3333;1" dur="15s" repeatCount="indefinite" /><rect x="635" y="202" width="80" height="24" rx="12" fill="#F87171" fillOpacity="0.15" /><text x="675" y="218" textAnchor="middle" fontFamily="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" fontSize="12" fill="#F87171">503 ✕</text></g>
    <g opacity="0"><animate attributeName="opacity" values="0;0;1;1;0;0" keyTimes="0;0.1933;0.2100;0.3633;0.3800;1" dur="15s" repeatCount="indefinite" /><circle cx="80" cy="228" r="11" fill="none" stroke="#FBBF24" strokeWidth="2" /><line x1="80" y1="228" x2="80" y2="221" stroke="#FBBF24" strokeWidth="2" strokeLinecap="round"><animateTransform attributeName="transform" type="rotate" from="0 80 228" to="360 80 228" dur="1s" repeatCount="indefinite" /></line><text x="100" y="232" fontFamily="ui-sans-serif, -apple-system, BlinkMacSystemFont, 'Segoe UI', Helvetica, Arial, sans-serif" fontSize="12" fill="#FBBF24">Retry #1 with backoff</text></g>
    <g opacity="0" filter="url(#whd-glow)"><animateMotion path="M 240 150 L 558 150" keyPoints="0;0;1;1" keyTimes="0;0.3733;0.4667;1" calcMode="linear" dur="15s" repeatCount="indefinite" /><animate attributeName="opacity" values="0;0;1;1;0;0" keyTimes="0;0.3733;0.3800;0.4767;0.4933;1" dur="15s" repeatCount="indefinite" /><rect x="-26" y="-17" width="52" height="34" rx="5" fill="#1C1917" stroke="#34D399" strokeWidth="2" /><path d="M -24 -14 L 0 4 L 24 -14" fill="none" stroke="#34D399" strokeWidth="2" strokeLinejoin="round" /></g>
    <g opacity="0"><animate attributeName="opacity" values="0;0;1;1" keyTimes="0;0.4867;0.5000;1" dur="15s" repeatCount="indefinite" /><path d="M 354 212 l 4 4 l 8 -9" fill="none" stroke="#34D399" strokeWidth="2.2" strokeLinecap="round" strokeLinejoin="round" /><text x="372" y="216" fontFamily="ui-sans-serif, -apple-system, BlinkMacSystemFont, 'Segoe UI', Helvetica, Arial, sans-serif" fontSize="12" fill="#F5F5F4">timestamp within 5 minutes</text></g>
    <g opacity="0"><animate attributeName="opacity" values="0;0;1;1" keyTimes="0;0.5233;0.5367;1" dur="15s" repeatCount="indefinite" /><path d="M 354 234 l 4 4 l 8 -9" fill="none" stroke="#34D399" strokeWidth="2.2" strokeLinecap="round" strokeLinejoin="round" /><text x="372" y="238" fontFamily="ui-sans-serif, -apple-system, BlinkMacSystemFont, 'Segoe UI', Helvetica, Arial, sans-serif" fontSize="12" fill="#F5F5F4">HMAC-SHA256 signature matches</text></g>
    <g opacity="0"><animate attributeName="opacity" values="0;0;1;1" keyTimes="0;0.5600;0.5733;1" dur="15s" repeatCount="indefinite" /><path d="M 354 256 l 4 4 l 8 -9" fill="none" stroke="#34D399" strokeWidth="2.2" strokeLinecap="round" strokeLinejoin="round" /><text x="372" y="260" fontFamily="ui-sans-serif, -apple-system, BlinkMacSystemFont, 'Segoe UI', Helvetica, Arial, sans-serif" fontSize="12" fill="#F5F5F4">new X-Fortis-Delivery id</text></g>
    <g opacity="0"><animate attributeName="opacity" values="0;0;1;1" keyTimes="0;0.6067;0.6200;1" dur="15s" repeatCount="indefinite" /><rect x="635" y="202" width="80" height="24" rx="12" fill="#34D399" fillOpacity="0.15" /><text x="675" y="218" textAnchor="middle" fontFamily="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" fontSize="12" fill="#34D399">200 OK</text></g>
    <circle r="5" fill="#34D399" filter="url(#whd-glow)" opacity="0"><animateMotion path="M 584 150 L 216 150" keyPoints="0;0;1;1" keyTimes="0;0.6200;0.6800;1" calcMode="linear" dur="15s" repeatCount="indefinite" /><animate attributeName="opacity" values="0;0;1;1;0;0" keyTimes="0;0.6200;0.6280;0.6800;0.6880;1" dur="15s" repeatCount="indefinite" /></circle>
    <g opacity="0"><animate attributeName="opacity" values="0;0;1;1" keyTimes="0;0.6800;0.6967;1" dur="15s" repeatCount="indefinite" /><text x="125.0" y="220" textAnchor="middle" fontFamily="ui-sans-serif, -apple-system, BlinkMacSystemFont, 'Segoe UI', Helvetica, Arial, sans-serif" fontSize="12" fontWeight="600" fill="#34D399">Delivered ✓</text></g>
    </g>
    </svg>;

export const TranslationLifecycleDiagram = () => <svg style={{
  width: "100%",
  height: "auto",
  display: "block"
}} xmlns="http://www.w3.org/2000/svg" viewBox="0 0 800 400" role="img" aria-labelledby="trl-t trl-d">
    <title id="trl-t">Translation via the Fortis API</title>
    <desc id="trl-d">A source document moves through upload, quote, approval, translation and delivery. At each stage Fortis sends a webhook event to your endpoint.</desc>
    <defs>
      <filter id="trl-glow" x="-100%" y="-100%" width="300%" height="300%">
        <feGaussianBlur stdDeviation="3.5" result="b" />
        <feMerge><feMergeNode in="b" /><feMergeNode in="SourceGraphic" /></feMerge>
      </filter>
      <pattern id="trl-dots" width="20" height="20" patternUnits="userSpaceOnUse">
        <circle cx="1" cy="1" r="1" fill="#FFFFFF" opacity="0.045" />
      </pattern>
    </defs>
    <rect width="800" height="400" rx="14" fill="#1C1917" />
    <rect width="800" height="400" rx="14" fill="url(#trl-dots)" />
    
    <text x="32" y="38" fontFamily="ui-sans-serif, -apple-system, BlinkMacSystemFont, 'Segoe UI', Helvetica, Arial, sans-serif" fontSize="12" fontWeight="600" letterSpacing="1.4" fill="#A8A29E">TRANSLATION · EVENTS YOUR ENDPOINT RECEIVES</text>
    <rect x="150" y="62" width="500" height="46" rx="10" fill="#292524" stroke="#44403C" />
    <rect x="150" y="62" width="500" height="46" rx="10" fill="none" strokeWidth="1.5" stroke="#44403C"><animate attributeName="stroke" values="#44403C;#44403C;#34D399;#44403C;#44403C;#34D399;#44403C;#44403C;#34D399;#44403C;#44403C;#34D399;#44403C;#44403C;#34D399;#44403C;#44403C" keyTimes="0;0.0941;0.0971;0.1353;0.2353;0.2382;0.2765;0.3765;0.3794;0.4176;0.5176;0.5206;0.5588;0.7176;0.7206;0.7588;1" dur="17s" repeatCount="indefinite" /></rect>
    <circle cx="172" cy="85" r="4" fill="#34D399"><animate attributeName="opacity" values="1;0.35;1" dur="1.6s" repeatCount="indefinite" /></circle>
    <text x="186" y="89" fontFamily="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" fontSize="12.5" fill="#A8A29E">POST https://example.com/webhooks/fortis</text>
    <text x="632" y="90" textAnchor="end" fontFamily="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" fontSize="13" fontWeight="600" fill="#34D399" opacity="0">file.ready<animate attributeName="opacity" values="0;0;1;1;0;0" keyTimes="0;0.0941;0.1029;0.2265;0.2353;1" dur="17s" repeatCount="indefinite" /></text>
    <text x="632" y="90" textAnchor="end" fontFamily="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" fontSize="13" fontWeight="600" fill="#34D399" opacity="0">quote.ready<animate attributeName="opacity" values="0;0;1;1;0;0" keyTimes="0;0.2353;0.2441;0.3676;0.3765;1" dur="17s" repeatCount="indefinite" /></text>
    <text x="632" y="90" textAnchor="end" fontFamily="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" fontSize="13" fontWeight="600" fill="#34D399" opacity="0">order.created<animate attributeName="opacity" values="0;0;1;1;0;0" keyTimes="0;0.3765;0.3853;0.5088;0.5176;1" dur="17s" repeatCount="indefinite" /></text>
    <text x="632" y="90" textAnchor="end" fontFamily="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" fontSize="13" fontWeight="600" fill="#34D399" opacity="0">order.in_progress<animate attributeName="opacity" values="0;0;1;1;0;0" keyTimes="0;0.5176;0.5265;0.7088;0.7176;1" dur="17s" repeatCount="indefinite" /></text>
    <text x="632" y="90" textAnchor="end" fontFamily="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" fontSize="13" fontWeight="600" fill="#34D399" opacity="0">order.completed<animate attributeName="opacity" values="0;0;1;1;0;0" keyTimes="0;0.7176;0.7265;0.9559;0.9647;1" dur="17s" repeatCount="indefinite" /></text>
    <line x1="100" y1="280" x2="700" y2="280" stroke="#44403C" strokeWidth="3" strokeLinecap="round" />
    <g><animate attributeName="opacity" values="1;1;0;0" keyTimes="0;0.9294;0.9647;1" dur="17s" repeatCount="indefinite" />
    <line x1="100" y1="280" x2="700" y2="280" stroke="#34D399" strokeWidth="3" strokeLinecap="round" strokeDasharray="600" strokeDashoffset="600"><animate attributeName="stroke-dashoffset" values="600;600;600;450;450;300;300;150;150;0;0" keyTimes="0;0.0294;0.1235;0.1706;0.2647;0.3118;0.4059;0.4529;0.6059;0.6529;1" dur="17s" repeatCount="indefinite" /></line>
    </g>
    <circle cx="100" cy="280" r="9" fill="#1C1917" stroke="#44403C" strokeWidth="3"><animate attributeName="stroke" values="#44403C;#44403C;#34D399;#34D399;#44403C;#44403C" keyTimes="0;0.0294;0.0412;0.9294;0.9647;1" dur="17s" repeatCount="indefinite" /></circle>
    <text x="100" y="350" textAnchor="middle" fontFamily="ui-sans-serif, -apple-system, BlinkMacSystemFont, 'Segoe UI', Helvetica, Arial, sans-serif" fontSize="14" fontWeight="600" fill="#A8A29E">Upload<animate attributeName="fill" values="#A8A29E;#A8A29E;#F5F5F4;#F5F5F4;#A8A29E;#A8A29E" keyTimes="0;0.0294;0.0412;0.9294;0.9647;1" dur="17s" repeatCount="indefinite" /></text>
    <text x="100" y="370" textAnchor="middle" fontFamily="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" fontSize="11.5" fill="#A8A29E">POST /v1/files</text>
    <circle cx="250" cy="280" r="9" fill="#1C1917" stroke="#44403C" strokeWidth="3"><animate attributeName="stroke" values="#44403C;#44403C;#34D399;#34D399;#44403C;#44403C" keyTimes="0;0.1706;0.1824;0.9294;0.9647;1" dur="17s" repeatCount="indefinite" /></circle>
    <text x="250" y="350" textAnchor="middle" fontFamily="ui-sans-serif, -apple-system, BlinkMacSystemFont, 'Segoe UI', Helvetica, Arial, sans-serif" fontSize="14" fontWeight="600" fill="#A8A29E">Quote<animate attributeName="fill" values="#A8A29E;#A8A29E;#F5F5F4;#F5F5F4;#A8A29E;#A8A29E" keyTimes="0;0.1706;0.1824;0.9294;0.9647;1" dur="17s" repeatCount="indefinite" /></text>
    <text x="250" y="370" textAnchor="middle" fontFamily="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" fontSize="11.5" fill="#A8A29E">POST /v1/quotes</text>
    <circle cx="400" cy="280" r="9" fill="#1C1917" stroke="#44403C" strokeWidth="3"><animate attributeName="stroke" values="#44403C;#44403C;#34D399;#34D399;#44403C;#44403C" keyTimes="0;0.3118;0.3235;0.9294;0.9647;1" dur="17s" repeatCount="indefinite" /></circle>
    <text x="400" y="350" textAnchor="middle" fontFamily="ui-sans-serif, -apple-system, BlinkMacSystemFont, 'Segoe UI', Helvetica, Arial, sans-serif" fontSize="14" fontWeight="600" fill="#A8A29E">Approve<animate attributeName="fill" values="#A8A29E;#A8A29E;#F5F5F4;#F5F5F4;#A8A29E;#A8A29E" keyTimes="0;0.3118;0.3235;0.9294;0.9647;1" dur="17s" repeatCount="indefinite" /></text>
    <text x="400" y="370" textAnchor="middle" fontFamily="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" fontSize="11.5" fill="#A8A29E">POST …/approve</text>
    <circle cx="550" cy="280" r="9" fill="#1C1917" stroke="#44403C" strokeWidth="3"><animate attributeName="stroke" values="#44403C;#44403C;#34D399;#34D399;#44403C;#44403C" keyTimes="0;0.4529;0.4647;0.9294;0.9647;1" dur="17s" repeatCount="indefinite" /></circle>
    <text x="550" y="350" textAnchor="middle" fontFamily="ui-sans-serif, -apple-system, BlinkMacSystemFont, 'Segoe UI', Helvetica, Arial, sans-serif" fontSize="14" fontWeight="600" fill="#A8A29E">Translate<animate attributeName="fill" values="#A8A29E;#A8A29E;#F5F5F4;#F5F5F4;#A8A29E;#A8A29E" keyTimes="0;0.4529;0.4647;0.9294;0.9647;1" dur="17s" repeatCount="indefinite" /></text>
    <text x="550" y="370" textAnchor="middle" fontFamily="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" fontSize="11.5" fill="#A8A29E">in progress</text>
    <circle cx="700" cy="280" r="9" fill="#1C1917" stroke="#44403C" strokeWidth="3"><animate attributeName="stroke" values="#44403C;#44403C;#34D399;#34D399;#44403C;#44403C" keyTimes="0;0.6529;0.6647;0.9294;0.9647;1" dur="17s" repeatCount="indefinite" /></circle>
    <text x="700" y="350" textAnchor="middle" fontFamily="ui-sans-serif, -apple-system, BlinkMacSystemFont, 'Segoe UI', Helvetica, Arial, sans-serif" fontSize="14" fontWeight="600" fill="#A8A29E">Deliver<animate attributeName="fill" values="#A8A29E;#A8A29E;#F5F5F4;#F5F5F4;#A8A29E;#A8A29E" keyTimes="0;0.6529;0.6647;0.9294;0.9647;1" dur="17s" repeatCount="indefinite" /></text>
    <text x="700" y="370" textAnchor="middle" fontFamily="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" fontSize="11.5" fill="#A8A29E">GET …/download</text>
    <path d="M 100 236 C 100 160, 190.0 192, 190.0 110" fill="none" stroke="#34D399" strokeWidth="1.5" strokeDasharray="4 5" opacity="0"><animate attributeName="opacity" values="0;0;0.9;0.9;0.28;0.28;0;0" keyTimes="0;0.0500;0.0559;0.0941;0.1294;0.9294;0.9647;1" dur="17s" repeatCount="indefinite" /><animate attributeName="stroke-dashoffset" values="18;0" dur="0.6s" repeatCount="indefinite" /></path>
    <circle r="5.5" fill="#34D399" filter="url(#trl-glow)" opacity="0"><animateMotion path="M 100 236 C 100 160, 190.0 192, 190.0 110" keyPoints="0;0;1;1" keyTimes="0;0.0500;0.0941;1" calcMode="linear" dur="17s" repeatCount="indefinite" /><animate attributeName="opacity" values="0;0;1;1;0;0" keyTimes="0;0.0500;0.0571;0.0941;0.1012;1" dur="17s" repeatCount="indefinite" /></circle>
    <circle cx="190.0" cy="108" r="4" fill="none" stroke="#34D399" strokeWidth="2" opacity="0"><animate attributeName="r" values="4;4;20;20" keyTimes="0;0.0941;0.1294;1" dur="17s" repeatCount="indefinite" /><animate attributeName="opacity" values="0;0;0.9;0;0" keyTimes="0;0.0941;0.0953;0.1294;1" dur="17s" repeatCount="indefinite" /></circle>
    <path d="M 250 236 C 250 160, 295.0 192, 295.0 110" fill="none" stroke="#34D399" strokeWidth="1.5" strokeDasharray="4 5" opacity="0"><animate attributeName="opacity" values="0;0;0.9;0.9;0.28;0.28;0;0" keyTimes="0;0.1912;0.1971;0.2353;0.2706;0.9294;0.9647;1" dur="17s" repeatCount="indefinite" /><animate attributeName="stroke-dashoffset" values="18;0" dur="0.6s" repeatCount="indefinite" /></path>
    <circle r="5.5" fill="#34D399" filter="url(#trl-glow)" opacity="0"><animateMotion path="M 250 236 C 250 160, 295.0 192, 295.0 110" keyPoints="0;0;1;1" keyTimes="0;0.1912;0.2353;1" calcMode="linear" dur="17s" repeatCount="indefinite" /><animate attributeName="opacity" values="0;0;1;1;0;0" keyTimes="0;0.1912;0.1982;0.2353;0.2424;1" dur="17s" repeatCount="indefinite" /></circle>
    <circle cx="295.0" cy="108" r="4" fill="none" stroke="#34D399" strokeWidth="2" opacity="0"><animate attributeName="r" values="4;4;20;20" keyTimes="0;0.2353;0.2706;1" dur="17s" repeatCount="indefinite" /><animate attributeName="opacity" values="0;0;0.9;0;0" keyTimes="0;0.2353;0.2365;0.2706;1" dur="17s" repeatCount="indefinite" /></circle>
    <path d="M 400 236 C 400 160, 400.0 192, 400.0 110" fill="none" stroke="#34D399" strokeWidth="1.5" strokeDasharray="4 5" opacity="0"><animate attributeName="opacity" values="0;0;0.9;0.9;0.28;0.28;0;0" keyTimes="0;0.3324;0.3382;0.3765;0.4118;0.9294;0.9647;1" dur="17s" repeatCount="indefinite" /><animate attributeName="stroke-dashoffset" values="18;0" dur="0.6s" repeatCount="indefinite" /></path>
    <circle r="5.5" fill="#34D399" filter="url(#trl-glow)" opacity="0"><animateMotion path="M 400 236 C 400 160, 400.0 192, 400.0 110" keyPoints="0;0;1;1" keyTimes="0;0.3324;0.3765;1" calcMode="linear" dur="17s" repeatCount="indefinite" /><animate attributeName="opacity" values="0;0;1;1;0;0" keyTimes="0;0.3324;0.3394;0.3765;0.3835;1" dur="17s" repeatCount="indefinite" /></circle>
    <circle cx="400.0" cy="108" r="4" fill="none" stroke="#34D399" strokeWidth="2" opacity="0"><animate attributeName="r" values="4;4;20;20" keyTimes="0;0.3765;0.4118;1" dur="17s" repeatCount="indefinite" /><animate attributeName="opacity" values="0;0;0.9;0;0" keyTimes="0;0.3765;0.3776;0.4118;1" dur="17s" repeatCount="indefinite" /></circle>
    <path d="M 550 236 C 550 160, 505.0 192, 505.0 110" fill="none" stroke="#34D399" strokeWidth="1.5" strokeDasharray="4 5" opacity="0"><animate attributeName="opacity" values="0;0;0.9;0.9;0.28;0.28;0;0" keyTimes="0;0.4735;0.4794;0.5176;0.5529;0.9294;0.9647;1" dur="17s" repeatCount="indefinite" /><animate attributeName="stroke-dashoffset" values="18;0" dur="0.6s" repeatCount="indefinite" /></path>
    <circle r="5.5" fill="#34D399" filter="url(#trl-glow)" opacity="0"><animateMotion path="M 550 236 C 550 160, 505.0 192, 505.0 110" keyPoints="0;0;1;1" keyTimes="0;0.4735;0.5176;1" calcMode="linear" dur="17s" repeatCount="indefinite" /><animate attributeName="opacity" values="0;0;1;1;0;0" keyTimes="0;0.4735;0.4806;0.5176;0.5247;1" dur="17s" repeatCount="indefinite" /></circle>
    <circle cx="505.0" cy="108" r="4" fill="none" stroke="#34D399" strokeWidth="2" opacity="0"><animate attributeName="r" values="4;4;20;20" keyTimes="0;0.5176;0.5529;1" dur="17s" repeatCount="indefinite" /><animate attributeName="opacity" values="0;0;0.9;0;0" keyTimes="0;0.5176;0.5188;0.5529;1" dur="17s" repeatCount="indefinite" /></circle>
    <path d="M 700 236 C 700 160, 610.0 192, 610.0 110" fill="none" stroke="#34D399" strokeWidth="1.5" strokeDasharray="4 5" opacity="0"><animate attributeName="opacity" values="0;0;0.9;0.9;0.28;0.28;0;0" keyTimes="0;0.6735;0.6794;0.7176;0.7529;0.9294;0.9647;1" dur="17s" repeatCount="indefinite" /><animate attributeName="stroke-dashoffset" values="18;0" dur="0.6s" repeatCount="indefinite" /></path>
    <circle r="5.5" fill="#34D399" filter="url(#trl-glow)" opacity="0"><animateMotion path="M 700 236 C 700 160, 610.0 192, 610.0 110" keyPoints="0;0;1;1" keyTimes="0;0.6735;0.7176;1" calcMode="linear" dur="17s" repeatCount="indefinite" /><animate attributeName="opacity" values="0;0;1;1;0;0" keyTimes="0;0.6735;0.6806;0.7176;0.7247;1" dur="17s" repeatCount="indefinite" /></circle>
    <circle cx="610.0" cy="108" r="4" fill="none" stroke="#34D399" strokeWidth="2" opacity="0"><animate attributeName="r" values="4;4;20;20" keyTimes="0;0.7176;0.7529;1" dur="17s" repeatCount="indefinite" /><animate attributeName="opacity" values="0;0;0.9;0;0" keyTimes="0;0.7176;0.7188;0.7529;1" dur="17s" repeatCount="indefinite" /></circle>
    <g><animateTransform attributeName="transform" type="translate" values="100 0;100 0;100 0;250 0;250 0;400 0;400 0;550 0;550 0;700 0;700 0" keyTimes="0;0.0294;0.1235;0.1706;0.2647;0.3118;0.4059;0.4529;0.6059;0.6529;1" dur="17s" repeatCount="indefinite" calcMode="spline" keySplines="0.45 0 0.25 1;0.45 0 0.25 1;0.45 0 0.25 1;0.45 0 0.25 1;0.45 0 0.25 1;0.45 0 0.25 1;0.45 0 0.25 1;0.45 0 0.25 1;0.45 0 0.25 1;0.45 0 0.25 1" />
    <g opacity="0"><animate attributeName="opacity" values="0;0;1;1;0;0" keyTimes="0;0.0059;0.0294;0.9294;0.9647;1" dur="17s" repeatCount="indefinite" />
    <clipPath id="trl-card"><rect x="-30.0" y="242.0" width="60" height="76" rx="7" /></clipPath>
    <rect x="-30.0" y="242.0" width="60" height="76" rx="7" fill="#FAFAF9" stroke="#44403C" />
    <rect x="-21.0" y="272.0" width="36" height="3.5" rx="1.75" fill="#D6D3D1"><animate attributeName="fill" values="#D6D3D1;#D6D3D1;#6EE7B7;#6EE7B7" keyTimes="0;0.4882;0.5000;1" dur="17s" repeatCount="indefinite" /></rect>
    <rect x="-21.0" y="280.5" width="44" height="3.5" rx="1.75" fill="#D6D3D1"><animate attributeName="fill" values="#D6D3D1;#D6D3D1;#6EE7B7;#6EE7B7" keyTimes="0;0.5059;0.5176;1" dur="17s" repeatCount="indefinite" /></rect>
    <rect x="-21.0" y="289.0" width="30" height="3.5" rx="1.75" fill="#D6D3D1"><animate attributeName="fill" values="#D6D3D1;#D6D3D1;#6EE7B7;#6EE7B7" keyTimes="0;0.5235;0.5353;1" dur="17s" repeatCount="indefinite" /></rect>
    <rect x="-21.0" y="297.5" width="40" height="3.5" rx="1.75" fill="#D6D3D1"><animate attributeName="fill" values="#D6D3D1;#D6D3D1;#6EE7B7;#6EE7B7" keyTimes="0;0.5412;0.5529;1" dur="17s" repeatCount="indefinite" /></rect>
    <rect x="-21.0" y="306.0" width="24" height="3.5" rx="1.75" fill="#D6D3D1"><animate attributeName="fill" values="#D6D3D1;#D6D3D1;#6EE7B7;#6EE7B7" keyTimes="0;0.5588;0.5706;1" dur="17s" repeatCount="indefinite" /></rect>
    <rect x="-22.0" y="251.0" width="26" height="14" rx="4" fill="#1C1917" />
    <text x="-9.0" y="261.5" textAnchor="middle" fontFamily="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" fontSize="9.5" fontWeight="700" fill="#FBBF24">ES<animate attributeName="opacity" values="1;1;0;0" keyTimes="0;0.5706;0.5824;1" dur="17s" repeatCount="indefinite" /></text>
    <text x="-9.0" y="261.5" textAnchor="middle" fontFamily="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" fontSize="9.5" fontWeight="700" fill="#34D399" opacity="0">EN<animate attributeName="opacity" values="0;0;1;1" keyTimes="0;0.5824;0.5941;1" dur="17s" repeatCount="indefinite" /></text>
    <g clipPath="url(#trl-card)"><rect x="-30.0" y="228.0" width="60" height="14" fill="#34D399" opacity="0"><animate attributeName="opacity" values="0;0;1;1;0;0" keyTimes="0;0.4647;0.4706;0.5706;0.5765;1" dur="17s" repeatCount="indefinite" /><animate attributeName="y" values="228.0;318.0" dur="0.8s" repeatCount="indefinite" /><animate attributeName="fill-opacity" values="0.1;0.35;0.1" dur="0.8s" repeatCount="indefinite" /></rect></g>
    <g opacity="0"><animate attributeName="opacity" values="0;0;1;1" keyTimes="0;0.6647;0.6765;1" dur="17s" repeatCount="indefinite" /><circle cx="28.0" cy="244.0" r="11" fill="#34D399" /><path d="M 23.0 244.0 l 4 4 l 7 -8" fill="none" stroke="#1C1917" strokeWidth="2.5" strokeLinecap="round" strokeLinejoin="round" strokeDasharray="20" strokeDashoffset="20"><animate attributeName="stroke-dashoffset" values="20;20;0;0" keyTimes="0;0.6735;0.6941;1" dur="17s" repeatCount="indefinite" /></path></g>
    </g></g>
    </svg>;

This guide shows how to send foreign-language documents for translation, approve
the price, and download the translated files. Webhooks tell your system when
each stage finishes.

<Frame>
  <TranslationLifecycleDiagram />
</Frame>

## Before you begin

You need:

* An **Internal app** API key (`sk-...`) with the `files:write`,
  `quotes:write`, `quotes:approve`, `orders:read`, `documents:read`, and
  `webhooks:manage` scopes. See [Authentication](/authentication).
* **Net-terms invoicing** on your account to approve quotes through the API.
  Contact [support](mailto:support@fortiseval.com) to turn it on.
* A webhook endpoint subscribed to `quote.*`, `order.*`, and `file.*`. See
  [Webhooks](/guides/webhooks) to set one up.

<Tip>
  Need an evaluation of foreign-language credentials? You don't need a separate
  translation order. Create an [evaluation quote](/guides/credential-evaluations)
  with `translation_preference: "translation_service"` and we translate the
  documents as part of the evaluation.
</Tip>

<Steps>
  <Step title="Send the documents">
    Upload each file you want translated:

    ```bash theme={null}
    curl https://api.fortiseval.com/v1/files \
      -H "Authorization: Bearer sk-your-secret-key" \
      -F "file=@/path/to/acta-de-nacimiento.pdf"
    ```

    If the documents live on your own storage, ingest them by URL instead. We
    download them in the background and send `file.ready` (or `file.failed`) for
    each one:

    ```bash theme={null}
    curl https://api.fortiseval.com/v1/files \
      -H "Authorization: Bearer sk-your-secret-key" \
      -H "Content-Type: application/json" \
      -d '{ "url": "https://example.com/documents/acta-de-nacimiento.pdf" }'
    ```

    Wait until every file is `ready` before you create the quote. You can also skip
    this step and pass the URLs in `document_urls`.
  </Step>

  <Step title="Create a translation quote">
    Set `quote_type` to `translation` and name the language of the source documents:

    ```bash theme={null}
    curl https://api.fortiseval.com/v1/quotes \
      -H "Authorization: Bearer sk-your-secret-key" \
      -H "Content-Type: application/json" \
      -d '{
        "quote_type": "translation",
        "source_language": "Spanish",
        "target_language": "English",
        "turnaround": "standard",
        "document_ids": ["upload_Hc72pN...", "upload_Vb19qT..."],
        "candidate": { "first_name": "Emma", "last_name": "Caballero" },
        "reference": "CASE-2291",
        "notes": "Birth certificate and marriage certificate for an I-130 filing."
      }'
    ```

    The quote is created in status `requested`:

    ```json theme={null}
    {
      "message": "Quote request created successfully.",
      "data": {
        "id": "quot_Lr5eGh...",
        "status": "requested",
        "quote_type": "translation",
        "source_language": "spanish",
        "target_language": "english",
        "requested_turnaround": "standard",
        "total_cents": 0,
        "items": [],
        "documents": [
          { "id": "doc_Pw3kZc...", "file_name": "acta-de-nacimiento.pdf", "document_role": "source" },
          { "id": "doc_Tn8yUf...", "file_name": "acta-de-matrimonio.pdf", "document_role": "source" }
        ]
      }
    }
    ```

    Translation fields:

    | Field                                     | Required          | Description                                                      |
    | ----------------------------------------- | ----------------- | ---------------------------------------------------------------- |
    | `quote_type`                              | Yes               | `translation`                                                    |
    | `source_language`                         | Yes               | The language of the documents, such as `Spanish` or `Portuguese` |
    | `target_language`                         | No                | The language to translate into. Defaults to English              |
    | `document_ids` / `document_urls`          | Yes, at least one | Up to 50 documents in total                                      |
    | `turnaround`                              | No                | `standard`, `expedited`, `next_day`, or `same_day` (rush)        |
    | `candidate`                               | No                | The person named in the documents                                |
    | `reference`, `notes`, `cc_emails`, `team` | No                | Same as for [every quote](/guides/quotes#creating-quotes)        |

    You can give a language as its English name (`Spanish`), or as an ISO 639-1
    (`es`) or ISO 639-2 (`spa`) code. The quote returns it in a normalized form,
    such as `spanish`. If we don't recognize a value, we keep it as you sent it and
    our team confirms it when pricing the quote.

    If your documents are in more than one language, create one quote per source
    language.
  </Step>

  <Step title="Review the price">
    Our team reviews each document and prices the quote. When it's priced, the quote moves to `awaiting_payment` and you receive `quote.ready`.
    The `documents` on the quote carry what we found, including `page_count`,
    `word_count`, and the detected `source_language`:

    ```json theme={null}
    {
      "type": "quote.ready",
      "data": {
        "object": {
          "id": "quot_Lr5eGh...",
          "status": "awaiting_payment",
          "quote_type": "translation",
          "total_cents": 9000,
          "total": "90.00",
          "items": [
            {
              "service_name": "Translation",
              "quantity": 2,
              "price": 4500,
              "subtotal": 9000,
              "currency": "usd"
            }
          ]
        }
      }
    }
    ```
  </Step>

  <Step title="Approve the quote">
    ```bash theme={null}
    curl -X POST https://api.fortiseval.com/v1/quotes/quot_Lr5eGh.../approve \
      -H "Authorization: Bearer sk-your-secret-key"
    ```

    Approval creates the order (`ord_...`) and bills it to your open invoice. You
    receive `quote.approved` and `order.created`.
  </Step>

  <Step title="Collect the translations">
    You receive `order.in_progress` when a translator starts and `order.completed`
    when the translations are delivered. Fetch the order and pick the documents
    whose `document_role` is `translation`:

    ```bash theme={null}
    curl https://api.fortiseval.com/v1/orders/ord_Qm4tHs... \
      -H "Authorization: Bearer sk-your-secret-key"
    ```

    ```json theme={null}
    {
      "id": "ord_Qm4tHs...",
      "status": "completed",
      "documents": [
        { "id": "doc_Pw3kZc...", "file_name": "acta-de-nacimiento.pdf", "document_role": "source" },
        { "id": "doc_Xe6aRj...", "file_name": "acta-de-nacimiento_translated.pdf", "document_role": "translation" }
      ]
    }
    ```

    Then request a download URL for each translation. The URL is valid for one hour:

    ```bash theme={null}
    curl https://api.fortiseval.com/v1/documents/doc_Xe6aRj.../download \
      -H "Authorization: Bearer sk-your-secret-key"
    ```
  </Step>
</Steps>

## Handle the events

This handler follows a translation from upload to delivery. It records progress
on your case, then downloads each translation when the order completes. It uses
`verifyFortisSignature` from the [webhooks guide](/guides/webhooks#verifying-signatures).

<CodeGroup>
  ```javascript Node.js theme={null}
  import express from "express";

  const app = express();
  const API = "https://api.fortiseval.com/v1";
  const headers = { Authorization: `Bearer ${process.env.FORTIS_API_KEY}` };

  app.post("/webhooks/fortis", express.raw({ type: "application/json" }), async (req, res) => {
    const payload = req.body.toString("utf8");

    if (!verifyFortisSignature(payload, req.get("X-Fortis-Signature"), process.env.FORTIS_WHSEC)) {
      return res.sendStatus(400);
    }

    res.sendStatus(200);

    const event = JSON.parse(payload);
    const object = event.data.object;

    switch (event.type) {
      case "file.failed":
        await flagUpload(object.id, object.failure_message); // your code
        break;

      case "quote.ready":
        await requestApproval(object.id, object.total); // your code, or approve right away
        break;

      case "order.in_progress":
        await setCaseStatus(object.id, "Translating"); // your code
        break;

      case "order.completed": {
        const order = await fetch(`${API}/orders/${object.id}`, { headers }).then((r) => r.json());

        for (const doc of order.documents.filter((d) => d.document_role === "translation")) {
          const { url, file_name } = await fetch(`${API}/documents/${doc.id}/download`, { headers })
            .then((r) => r.json());
          await saveTranslation(order.id, file_name, url); // your code
        }
        break;
      }
    }
  });
  ```

  ```php PHP theme={null}
  use Illuminate\Http\Request;
  use Illuminate\Support\Facades\Http;

  Route::post('/webhooks/fortis', function (Request $request) {
      $payload = $request->getContent();

      if (! verifyFortisSignature($payload, $request->header('X-Fortis-Signature', ''), config('services.fortis.whsec'))) {
          abort(400);
      }

      $event = json_decode($payload, true);
      $object = $event['data']['object'];

      if ($event['type'] === 'order.completed') {
          dispatch(function () use ($object) {
              $api = Http::withToken(config('services.fortis.key'))->baseUrl('https://api.fortiseval.com/v1');

              $translations = collect($api->get("orders/{$object['id']}")->json('documents'))
                  ->where('document_role', 'translation');

              foreach ($translations as $document) {
                  $download = $api->get("documents/{$document['id']}/download")->json();
                  // Store $download['url'] before it expires
              }
          });
      }

      return response()->noContent();
  });
  ```
</CodeGroup>

<Note>
  Deliveries can arrive out of order or more than once. Deduplicate on the
  `X-Fortis-Delivery` header, and fetch the order when you need its current
  state.
</Note>

## How a webhook reaches you

Every event is signed. If your endpoint is down, we retry with backoff until it
answers:

<Frame>
  <WebhookDeliveryDiagram />
</Frame>

See [Webhooks](/guides/webhooks) for the signature scheme, retry schedule, and
endpoint requirements.
