Skip to main content
GuideServerConfigure and deploy Tabsdata servers on your machine.TutorialsConfigure data integration workflows within a running Tabsdata server.Advanced TutorialsBuild end-to-end workflows between two specific systems.API ReferenceCLI ReferenceRelease Notes
Version: 2.0.0

Publish data from Salesforce Reports

Ingest a Salesforce report

A report publisher names a report that already exists in Salesforce rather than a query you write. The report's own definition decides which columns come back, so the publisher adds filters instead of selecting columns.

warning

There are several ways to pass secrets into Tabsdata. See Secrets Management for more info if you are planning to have an LLM register secrets into Tabsdata's credential store.

Can you publish the contacts report out of salesforce, US and France rows only

Listed 1 directory, called tabsdata 3 times (ctrl+o to expand)

The Salesforce Reports publisher guide and connection template are loaded. Four values left, then I generate and register. Reply with them one per line, or point me at a file that has them:

  • collection name
  • username, password and security token
  • report name or id
  • filters, and how they combine
everything you need is in

Read 1 file, called tabsdata 2 times (ctrl+o to expand)

All three credentials found. Reading back what is not secret: report contacts, column names from columnName, two country filters combined as 1 or 2, collection sf_reports.

Called tabsdata 4 times (ctrl+o to expand)

Connection written to , and collection sf_reports created from it in group sources. Connection check passed, and the report resolved by name.

Called tabsdata 2 times (ctrl+o to expand)

The publisher, registered in sf_reports. column_name_strategy="columnName" means the filters have to use the report's columnName values, so COUNTRY rather than the label a reader sees in the UI:

@publisher(
source=SalesforceReportSrc(
report=["contacts"],
column_name_strategy="columnName",
filter=[
("COUNTRY", "equals", "USA"),
("COUNTRY", "equals", "France"),
],
filter_logic="1 or 2",
),
output_tables=["contacts"],
)
def read_contacts(contacts: TableFramesSpec) -> tuple[TableFrameSpec]:
return (contacts[0],)

Called tabsdata 6 times (ctrl+o to expand)

Execution committed, 0 failures. The filters ran on the Salesforce side, so only matching rows crossed the wire.

sf_reports/contacts is live, holding the report's own columns named from columnName:

┌─────────────────┬────────────┐
      field         value    
├─────────────────┼────────────┤
 collection       sf_reports 
├─────────────────┼────────────┤
 table            contacts   
├─────────────────┼────────────┤
 report           contacts   
├─────────────────┼────────────┤
 rows             2,341      
├─────────────────┼────────────┤
 columns          9          
├─────────────────┼────────────┤
 filters applied  1 or 2     
└─────────────────┴────────────┘

filter_logic refers to filters by position, so 1 is the first filter in the list and 2 the second. "1 or (2 and 3)" groups three of them. Without it, the filters carry no stated relationship.

A report referenced by name uses what Salesforce calls the report unique name, also shown as the developer name, rather than the label in the UI. find_report_by states whether a value is an id or a name when the inference would guess wrong.

For an incremental read, last_modified_column names the column the comparison runs against, and that column has to be part of the report definition.