Most engineers never think about document workflows until they (or a teammate on a relocation package) have to deal with one personally. Then suddenly you're staring at a checklist of apostilles, sworn translations, and consulate-specific requirements with zero tooling to manage any of it.

I went through this recently while helping set up an internal process for a company relocating engineers to Spain. The legal and procedural side is well covered elsewhere — there's a solid breakdown of document requirements and translation types in this article on certified translation for Spanish work visas. What's missing from most of these guides is the operational side: how do you actually track, version, and validate a pile of multilingual legal documents without losing your mind or missing a deadline?

This post covers how to think about that as an engineering problem, not just an HR problem.

Why this is a pipeline problem, not a checklist problem

A visa document set has properties that should feel familiar if you've built any kind of data pipeline:

  • Multiple source formats (PDF scans, photos, government portals exporting weird formats)
  • Transformation steps with hard dependencies (apostille before or in parallel with translation, translation before submission)
  • Validation gates (consulate-specific requirements that change the acceptance criteria)
  • Expiry windows (criminal record certificates are often only valid for 90 days)
  • Versioning (a translated contract might need updates if the original changes)

Treating this like a checklist in a spreadsheet works for one person, one time. It falls apart the moment you're coordinating five relocations at once, which is exactly when most companies start caring about it.

Modeling the document state machine

Each document in the pipeline goes through a predictable set of states. A simple state machine makes the tracking explicit instead of living in someone's head:

REQUESTED -> ISSUED -> APOSTILLE_PENDING -> APOSTILLED \-> TRANSLATION_PENDING -> TRANSLATED -> SUBMITTED -> ACCEPTED/REJECTED 

Note the branch: apostille and translation can happen in parallel, as the source article points out. Most teams serialize this unnecessarily and burn a week waiting for the apostille to come back before even starting the translation. If your tracking tool (even a Notion board or an Airtable) doesn't model that fork, you're adding latency for no reason.

A minimal schema for tracking this, if you're building something lightweight:

from dataclasses import dataclass, field from datetime import date from enum import Enum class DocStatus(Enum): REQUESTED = "requested" ISSUED = "issued" APOSTILLE_PENDING = "apostille_pending" APOSTILLED = "apostilled" TRANSLATION_PENDING = "translation_pending" TRANSLATED = "translated" SUBMITTED = "submitted" ACCEPTED = "accepted" REJECTED = "rejected" @dataclass class VisaDocument: doc_type: str # e.g. "criminal_record", "employment_contract"  applicant_id: str source_lang: str target_lang: str requires_sworn_translation: bool issued_date: date | None = None validity_days: int | None = None status: DocStatus = DocStatus.REQUESTED history: list[tuple[DocStatus, date]] = field(default_factory=list) def is_expired(self, as_of: date) -> bool: if not self.issued_date or not self.validity_days: return False return (as_of - self.issued_date).days > self.validity_days 

The requires_sworn_translation flag matters more than it looks. As the source article notes, a certified translation from a non-appointed translator won't satisfy a requirement for traducción jurada. If you're building any kind of validation logic around document acceptance, this distinction needs to be a first-class field, not an afterthought in a comment.

Automating the parts that don't need a human

You can't automate the actual translation (nor should you try — machine translation has no legal standing here, full stop). But plenty of surrounding work is automatable:

Expiry tracking. A daily cron job that flags documents approaching their validity window is trivial to build and saves real pain:

from datetime import date, timedelta def documents_expiring_soon(docs: list[VisaDocument], days_ahead: int = 14): today = date.today() return [ d for d in docs if d.issued_date and d.validity_days and 0 < (d.issued_date + timedelta(days=d.validity_days) - today).days <= days_ahead ] 

Requirement diffing. Consulate requirements change and differ by jurisdiction. If you're handling more than a handful of applications, maintain a simple JSON config per consulate and diff it against your document checklist rather than relying on memory:

{ "consulate": "Madrid", "visa_type": "work_visa_highly_qualified", "required_docs": [ {"type": "criminal_record", "translation": "certified"}, {"type": "academic_degree", "translation": "sworn"}, {"type": "employment_contract", "translation": "certified"} ] } 

A quick script that cross-references this against your document tracker will catch mismatches (like a degree certificate with only a certified translation when sworn is required) before submission, not after rejection.

Scan quality gates. If your translation vendor works from scans, a bad scan is a silent failure mode. A basic check with something like pytesseract or opencv to validate resolution and completeness before handoff can prevent a translation redo cycle:

import cv2 def scan_is_usable(image_path: str, min_dpi_equiv: int = 1500) -> bool: img = cv2.imread(image_path) if img is None: return False height, width = img.shape[:2] return min(height, width) >= min_dpi_equiv 

This isn't rigorous DPI detection, but it catches the common case of someone uploading a blurry phone photo that will bounce back from the translator a day later.

Where humans still have to be in the loop

Don't try to automate:

  • Determining whether a document needs sworn vs. certified translation — this depends on the receiving authority and changes over time
  • Interpreting rejection reasons from a consulate
  • Anything involving apostille submission to a foreign ministry or court, since these are manual processes with no API

The goal of tooling here isn't to remove the legal/administrative expertise, it's to remove the friction around tracking state, catching expiry windows, and preventing avoidable mismatches between document type and translation format.

Takeaway

If you're responsible for coordinating relocation or visa logistics at a company with any scale, treat it like the pipeline it actually is. A lightweight state machine, an expiry checker, and a requirements diff tool will save more time than any spreadsheet template, and they'll scale past the first relocation case without anyone having to remember which consulate wanted what.

For the legal and procedural details on document types, certified vs. sworn translation, and apostille requirements specifically for Spain, see the original breakdown here: Certified Translation for Work Visa Spain.