import os import sys import requests class FolkAPI: BASE_URL = "https://api.folk.app/v1" def __init__(self, api_key: str): self.headers = {"Authorization": f"Bearer {api_key}"} def get_groups(self): """Fetch all groups from Folk.""" url = f"{self.BASE_URL}/groups" response = requests.get(url, headers=self.headers) response.raise_for_status() return response.json() def create_company( self, name: str, group_id: str = None, website: str = None, linkedin_url: str = None, description: str = None, emails=None, phones=None, addresses=None, urls=None, custom_field_values=None, groups=None, **kwargs, ): """Create a company (investor) in a specific group. This method builds a payload matching Folk's Create Company API: https://developer.folk.app/api-reference/companies/create-a-company It keeps backward compatibility with the previous `group_id`, `website` and `linkedin_url` arguments. """ url = f"{self.BASE_URL}/companies" # Build the top-level payload expected by Folk data = {"name": name} if description: data["description"] = description # Groups: prefer explicit `groups`, else fall back to `group_id` if groups: # Accept either list of ids or list of dicts formatted = [] for g in groups: if isinstance(g, dict) and g.get("id"): formatted.append({"id": g["id"]}) else: formatted.append({"id": str(g)}) data["groups"] = formatted elif group_id: data["groups"] = [{"id": group_id}] # Helper to normalize single or multiple inputs into lists def _to_list(val): if val is None: return None if isinstance(val, (list, tuple)): return [v for v in val if v is not None] return [val] # URLs: include website and linkedin_url if provided and merge with urls urls_list = _to_list(urls) or [] if website: urls_list.append(website) if linkedin_url: urls_list.append(linkedin_url) if urls_list: data["urls"] = urls_list # Emails/phones/addresses emails_list = _to_list(emails) if emails_list: data["emails"] = emails_list phones_list = _to_list(phones) if phones_list: data["phones"] = phones_list addresses_list = _to_list(addresses) if addresses_list: data["addresses"] = addresses_list # Custom field values follow the API's structure if custom_field_values: data["customFieldValues"] = custom_field_values # Allow passing any additional top-level fields via kwargs (careful) for k, v in kwargs.items(): # don't overwrite keys we explicitly set if k not in data: data[k] = v response = requests.post(url, headers=self.headers, json=data) response.raise_for_status() return response.json() def create_person( self, first_name: str, last_name: str, email: str = None, company_id: str = None, group_id: str = None, companies=None, emails=None, phones=None, addresses=None, urls=None, custom_field_values=None, groups=None, **kwargs, ): """Create a person in the workspace. Builds payload matching Folk's Create Person API: use camelCase keys (firstName, lastName, groups, companies, emails, etc.). Keeps backward compatibility with `company_id` and `group_id`. """ url = f"{self.BASE_URL}/people" data = {"firstName": first_name, "lastName": last_name} # Groups: explicit `groups` preferred, else fallback to `group_id` if groups: formatted = [] for g in groups: if isinstance(g, dict) and g.get("id"): formatted.append({"id": g["id"]}) else: formatted.append({"id": str(g)}) data["groups"] = formatted elif group_id: data["groups"] = [{"id": group_id}] # Companies: keep backward compatibility with company_id if companies: formatted = [] for c in companies: if isinstance(c, dict): formatted.append(c) elif isinstance(c, str): # treat as id formatted.append({"id": c}) if formatted: data["companies"] = formatted elif company_id: data["companies"] = [{"id": company_id}] # Helper to normalize into lists def _to_list(val): if val is None: return None if isinstance(val, (list, tuple)): return [v for v in val if v is not None] return [val] emails_list = _to_list(emails) or [] if email: emails_list.insert(0, email) if emails_list: data["emails"] = emails_list phones_list = _to_list(phones) if phones_list: data["phones"] = phones_list addresses_list = _to_list(addresses) if addresses_list: data["addresses"] = addresses_list urls_list = _to_list(urls) if urls_list: data["urls"] = urls_list if custom_field_values: data["customFieldValues"] = custom_field_values # Allow passthrough of other top-level fields in kwargs for k, v in kwargs.items(): if k not in data: data[k] = v response = requests.post(url, headers=self.headers, json=data) response.raise_for_status() return response.json() # Prefer getting the API key from the environment. If not set, fall back to the # existing (hard-coded) key so behavior is unchanged for now. DEFAULT_API_KEY = "FOLKfIGXuv74ML9EAajxyiUR39ePaNrZ" api_key = os.environ.get("FOLK_API_KEY", DEFAULT_API_KEY) folk = FolkAPI(api_key=api_key) def example_flow(): # Step 1: Get groups groups = folk.get_groups() print(groups) # Safely dig into the returned structure. The API returns groups under # groups['data']['items'] (not groups['data'][0]). Handle missing/empty. items = groups.get("data", {}).get("items", []) if not items: print("No groups returned by Folk API.") sys.exit(1) # Choose the first group as an example group_id = items[0].get("id") if not group_id: print("No id found for the first group item.") sys.exit(1) # Step 2: Choose a group_id and create a company company = folk.create_company( name="2050 Investment Partners", group_id=group_id, website="https://2050.com", linkedin_url="https://linkedin.com/company/2050-investments", ) # Step 3: Add a person to the same group or company person = folk.create_person( first_name="John", last_name="Doe", email="john@2050.com", company_id=company.get("data", {}).get("id"), group_id=group_id, ) print("Created company:", company) print("Created person:", person) if __name__ == "__main__": try: example_flow() except requests.HTTPError as e: # Try to include response body for easier debugging if available resp = getattr(e, "response", None) if resp is not None: try: body = resp.text except Exception: body = "" print("HTTP error while talking to Folk API:", e) print("Response status:", resp.status_code) print("Response body:", body) else: print("HTTP error while talking to Folk API:", e) sys.exit(1) except Exception as e: # pragma: no cover - top-level safety print("Unexpected error:", e) sys.exit(1)