September 9, 2026
Changed
-
Contact context stopped being a curation chore.
contextConfig.useContactContextused to volunteer a summary built from the fields you had ticked as contact context, and everything else the contact held was invisible to the agent. It now gives the agent two things: a short platform-decided set of fields in its prompt, and a generated document it can query mid-call holding everything else the contact has a value for. Their documents (a CV, files you uploaded) are on the same knowledge base as before. Nothing about the agent switch changed, and no request shape you send changed. What changed is how much reaches the agent, and it is strictly more. -
includeInContactContextis removed from/contact-fields. It was a per-field allowlist that shipped empty, so the feature was effectively off for anyone who had not walked the catalog. Two properties replace it, and neither needs curating:Take it out of your payloads. SendingincludeInContactContexton a create or update is now a400, as any unknown property is:Beyond that, if you were setting it on a non-personal field you need do nothing: that field now reaches the agent by default, through the document. If you were setting it on a field markedisPii, that decision was migrated toallowPiiInContextand still holds. “By default” is not “always”. A field reaches the agent when it is notreadOnlyand either is notisPiior has been let past that:company_name,company_description,agent_nameandprofilearereadOnly, describe the caller rather than the person being called, and are in neither the prompt nor the document whatever you set on them. -
isPiinow enforces rather than defaults. A personal field is excluded from the prompt AND from the document, which is what keepsemailandphoneoff every call without anyone deciding.allowPiiInContextis the way past it, per field, and it puts the value in both halves — a queryable value is one the agent might never think to look up, and for something like an amount owed that is the whole point. SendingallowPiiInContexton a field that is notisPiiis a400rather than a silent no-op.
Notes
- A contact’s document is generated the first time a context-enabled agent is scheduled to call them, and refreshed before a later call only when a value or the catalog actually changed. A contact nobody calls costs nothing. An immediate ad-hoc call for a contact with no document yet runs prompt-only rather than waiting on one, and it is ready for the next call.
- A value written between generation and the dial is not in that call’s document. The window is bounded by the freshness check at dial time.
- The document follows the contact’s retention, exactly as their uploaded documents do: it is deleted, provider copy included, when the contact is deleted or reaches its GDPR expiry.
- It does not appear in
GET /contacts/{id}/documentsand does not count against the five uploads per contact. It is our projection of data you already hold, not a file you attached.
September 8, 2026
Behavior Changes
-
A conditional branch’s
conditionis now validated. It never was. A branch could be stored with a condition the platform could not read — atypeoutsideintentandexpression, anintentwith nodescription, anexpressionwith nofield— and the flow was accepted. Nothing told you, and the agent still took that branch on calls: the compiled instruction for it came out describing nothing, so the route was chosen arbitrarily. That is the failure this closes. Four new codes join theerrorsarray on Create Agent and Update Agent, each with thepathof the offending branch:That last one closes a hole of its own. Anullinbranchescounted toward the branch total and then every per-branch rule skipped it, so the flow validated clean and the failure surfaced later as a500while the prompt was being built. It is a422naming the branch now.What to check in your own integration. A payload that sent a condition of your own shape, or left one off, was accepted before and is a422now. Everything the API has ever documented forFlowBranchConditionstill passes unchanged,EXISTSstill needs novalue, andlogicalOperatoris still accepted and ignored. The branch you nominate asdefaultBranchis checked like the rest: its condition is not read while it is the fallthrough, but movingdefaultBranchlater would put it back in charge of a route. Agents already holding an unreadable condition were repaired rather than left to fail: each one became{ "type": "intent", "description": "<the branch's label>" }, the label being the only authored statement of what that route means. Their cached prompts were dropped so the next call compiles from the repaired flow. If a repaired branch does not route the way you meant, the label is what to rewrite.
September 7, 2026
Added
-
Contact documents: a contact can now hold documents an agent may look up on a call — a cover letter, application notes, and the contact’s own processed CV. Five endpoints under
/contacts/{id}/documents, mirroring the agent knowledge base exactly: list, start an upload (presigned URL), complete it, rename or deactivate, delete. Same request and response shapes, same file types, same 20 MiB per-file limit, same upload rate limit. Scopes areread:contacts,write:contactsanddelete:contacts.A contact’s CV shows up on its own. Once a CV has been processed, the contact lists a document withsource: "CV". You did not upload it and cannot delete or rename it here — it is produced from the anonymized text of the CV, which is what the agent can look up. You can deactivate it, and that is how you keep a particular contact’s CV out of calls. Two limits instead of one. A contact holds at most 5 uploaded documents (the CV does not count). On top of that, your company’s total contact-document storage is capped by plan, and both are reserved when the upload is started — pending uploads included — so parallel requests cannot all pass the same check. When the company ceiling is the problem, the400says so in numbers: -
sourceon knowledge documents: every document — agent or contact — now carriessource, either"UPLOAD"or"CV". On an agent’s knowledge base it is always"UPLOAD". Additive; nothing else about the shape changed. -
Whether documents reach a call is not decided by the API. One switch decides: the agent must have
contextConfig.useContactContextenabled. With it on, the contact’s active documents are attached to the call as thecandidateknowledge base and the agent is told they exist and may be looked up, never read aloud. With it off, the call runs exactly as before. Every other knowledge base (company,position,reference_documents) is unaffected. Contact documents follow the contact’s retention: they are deleted — stored files and provider copies included — when the contact reaches its GDPR expiry date. -
The contact-field catalog now ships thirty-six default fields instead of eleven:
GET /contact-fieldsreturns a seeded set covering identity, the contact’s employer, orders and invoices, the relationship, appointments and screening, so most of what you would have defined by hand already exists. Only three of the previous eleven (order_number,is_vip,lead_source) could hold a per-contact value at all; the rest resolve from the contact record, your company or the agent. The new rows are the ones an agent would actually say:job_title,organization,department,company_size,industry,website,city,secondary_phone,invoice_number,amount_due,due_date,renewal_date,contract_end_date,plan_name,deal_value,lead_status,owner_name,customer_since,last_purchase,tags,appointment_at,years_experience,notice_period,salary_expectationandavailability_date. Nothing breaks — the response is a list and you iterate it — but two things follow. If you render every field you get back as a form row, expect thirty-six rows. And most seeded fields are still yours to shadow: define your own field with the same key and it overrides the label and type for your company alone. That is the intended path forcompany_size,industryandlead_status, which are seeded asstringbecause one company’s option sets are not another’s. The eight reserved keys (first_name,last_name,email,phone,company_name,company_description,agent_name,profile) refuse even that, because the platform resolves them itself. Five of them default toincludeInContactContext, so an agent withcontextConfig.useContactContexton can refer to them without a token in the flow:lead_source,job_title,plan_name,owner_nameandlast_purchase. Everything else is off, because every field that defaults on lengthens every prompt on every call.Superseded on September 9:includeInContactContextis gone and there is no per-field opt-in any more. See that day’s entry.organizationis the contact’s employer;company_nameis yours.{{contact.company_name}}resolves the company placing the call and always has — it is labelled “Your company name” for that reason. An author writing “I see you work at{{contact.company_name}}” got a fluent wrong sentence with no signal, soorganizationnow exists for what that author meant. -
categoryon a contact field: a new response property onGET /contact-fieldsnaming which panel the field belongs to —identity,organisation,commercial,relationship,scheduling,recruiting,platformorcustom. The list is now ordered bycategoryfirst, thendisplayOrder, thenkey, so iterating the response and breaking on a change ofcategorygives you the same panels the dashboard shows. It is set by InstaView and not writable: a field you define is alwayscustom, and a field of yours that shadows a seeded one reports the seeded field’s category, sojob_titlestays underorganisationfor everybody even after you relabel it. BecausedisplayOrdernow only separates fields inside one category, each category’s numbering restarts at 10 rather than continuing one long sequence. If you were sorting ondisplayOrderalone, sort oncategoryfirst. -
A
tagsdata type:dataType: "tags"stores astring[], and it is the one type that accepts two shapes on the way in — an array, or one comma-separated string, so a single CSV cell holding several tags maps onto one field without your side splitting it first.Entries are trimmed, blanks are dropped ("vip,, renewal,"is two tags) and duplicates are removed case-insensitively, keeping the spelling that came first. An empty list is the erase:[],[""]and","all clear the field, exactly asnulland""do, so you never get a key present with an empty array behind it. A number, a boolean or an object is a422rather than a one-element list — a caller sending42for a tags field has a mapping bug, and a silent["42"]would be spoken on a call. The seededtagsfield uses it.enumis still the right type when the set is closed and a value outside it should be refused. -
CSV aliases on every writable seeded field, in English, Slovak and Czech —
renewal_datecarriesrenewal,renewsanddátum obnovenia;amount_duecarriesbalance,outstandinganddlžná suma. Nothing consumes them yet. The current CSV importer maps against a fixed list of built-in contact properties and does not readcsvAliases, so this changes no import behaviour today — it is catalog metadata, seeded so the catalog is complete before the contact-field import lands. Adding your own withPATCH /contact-fields/{id}does not touch InstaView’s. -
fieldson the contact resource itself:POST /contactsandPATCH /contacts/{id}now accept catalogued field values in the body, so creating a contact with its values is one request instead of two.Before this, the read of a contact’s values sat on the contact (GET /contacts/{id}returnsfields) while the write sat somewhere else — andPOST /contactsdropped any inbound values and always answeredfields: {}, so the one request where you most want to set them was the one that could not. Field values are contact attributes; the resource for a contact’s attributes is the contact. The shape is the flat oneGET /contacts/{id}already returns, so read and write agree.fieldsmerges andmetadatareplaces, in the same request body, on purpose:metadatais one opaque document you own,fieldsare individually catalogued values. -
The
/contacts/{id}/fieldssub-resource is gone.GETreturned a strict subset ofGET /contacts/{id}, and its write did exactly whatPATCH /contacts/{id}with onlyfieldsdoes — same scopes, same guards, same blast radius, since a partial update touches only what you send. One resource, one shape.?createMissingFields=truemoved with it and now hangs off both contact writes. Off by default: a key infieldsthat your catalog does not know is a422unless you ask for it to be defined, because a typo would otherwise mint a permanent field visible in every agent’s variable palette.
Behavior Changes
-
metadatano longer has a reserved key.metadata.contactVariablesused to be cut out of what you sent and cut out of what you were shown, because a contact’s field values were stored inside that same object. They are stored separately now, sometadatais once again exactly the document you put there,contactVariablesincluded.Nothing aboutfieldschanges, in either direction. A value still reaches{{contact.<key>}}only by being sent asfieldsand passing catalog validation, and replacingmetadatastill cannot disturb the values you have stored — that guarantee is stronger now, not weaker, because the two are no longer the same column. If you were avoiding the key to work around the old reservation, you no longer need to.
September 3, 2026
Improvements
-
idealAnsweris now optional on a flow question: aloopingquestion needs only itsquestiontext.POST /agentsandPATCH /agents/{id}no longer answer422 LOOPING_QUESTION_INCOMPLETEfor a question that omitsidealAnswer, which makes a question set with no right answers, an internal survey or a satisfaction check, expressible for the first time.idealAnswerwas only ever scoring metadata: it is not spoken, it never reaches the agent’s instructions, and scoring has always skipped a question that has none. Requiring it therefore forced callers to invent an ideal answer for a question that has none, and that invented value then decided whether the question looked scoreable. Nothing changes for a question that sends one, and the scoring rules are unchanged:importanceorweightopts a question into the match score, and a question with noidealAnswercannot be scored at all, so settingimportanceon one has no effect. The same relaxation applies in the visual builder, where the ideal answer field can now be left blank.
September 2, 2026
Improvements
-
Owners invited through the API can now set a password: creating or updating a company with
ownerEmailstill sends the invitation email, and for an address that has no InstaView account yet it now also creates the login identity and sends a separate set your password email from Auth0. Before this, the invitation was the only thing the new owner received. Its link leads to a page that asks them to sign in, and having never signed up anywhere they naturally reached for “Reset password” — which for an address with no account is answered with a success message and no email, deliberately, so that the reset form cannot be used to find out which addresses have accounts. The invitation was valid the whole time and there was no way to tell. Nothing changes in the request or the response, and nothing changes for anownerEmailthat already belongs to an InstaView user: they are assigned asOWNERas before, with no extra email. An address that already has a login but no company gets the invitation only, not a password email it did not ask for. -
The invitation page opens without signing in first:
/invite/acceptnow renders for a visitor with no session, shows which email address the invitation was issued for, and leads with Create an account and accept. Signing in is the second option, for people who already have one. Previously the link redirected straight to the sign-in form, so an invitee with no account saw neither the invitation nor a way to create one. Signing up with the invited address accepts the invitation automatically once the email is verified.